Completed
Push — release-2.1 ( aa21c4...7040ad )
by Mathias
09:20
created

other/upgrade.php (72 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

Code
1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines http://www.simplemachines.org
8
 * @copyright 2017 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.3',
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.3',
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.1',
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 = 30;
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
// All the steps in detail.
84
// Number,Name,Function,Progress Weight.
85
$upcontext['steps'] = array(
86
	0 => array(1, 'Login', 'WelcomeLogin', 2),
87
	1 => array(2, 'Upgrade Options', 'UpgradeOptions', 2),
88
	2 => array(3, 'Backup', 'BackupDatabase', 10),
89
	3 => array(4, 'Database Changes', 'DatabaseChanges', 50),
90
	4 => array(5, 'Convert to UTF-8', 'ConvertUtf8', 20),
91
	5 => array(6, 'Convert serialized strings to JSON', 'serialize_to_json', 10),
92
	6 => array(7, 'Delete Upgrade.php', 'DeleteUpgrade', 1),
93
);
94
// Just to remember which one has files in it.
95
$upcontext['database_step'] = 3;
96
@set_time_limit(600);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
97
if (!ini_get('safe_mode'))
98
{
99
	ini_set('mysql.connect_timeout', -1);
100
	ini_set('default_socket_timeout', 900);
101
}
102
// Clean the upgrade path if this is from the client.
103
if (!empty($_SERVER['argv']) && php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
104
	for ($i = 1; $i < $_SERVER['argc']; $i++)
105
	{
106
		if (preg_match('~^--path=(.+)$~', $_SERVER['argv'][$i], $match) != 0)
107
			$upgrade_path = substr($match[1], -1) == '/' ? substr($match[1], 0, -1) : $match[1];
108
	}
109
110
// Are we from the client?
111
if (php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
112
{
113
	$command_line = true;
114
	$disable_security = true;
115
}
116
else
117
	$command_line = false;
118
119
// Load this now just because we can.
120
require_once($upgrade_path . '/Settings.php');
121
122
// We don't use "-utf8" anymore...  Tweak the entry that may have been loaded by Settings.php
123
if (isset($language))
124
	$language = str_ireplace('-utf8', '', $language);
125
126
// Are we logged in?
127
if (isset($upgradeData))
128
{
129
	$upcontext['user'] = json_decode(base64_decode($upgradeData), true);
130
131
	// Check for sensible values.
132 View Code Duplication
	if (empty($upcontext['user']['started']) || $upcontext['user']['started'] < time() - 86400)
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
133
		$upcontext['user']['started'] = time();
134 View Code Duplication
	if (empty($upcontext['user']['updated']) || $upcontext['user']['updated'] < time() - 86400)
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
135
		$upcontext['user']['updated'] = 0;
136
137
	$upcontext['started'] = $upcontext['user']['started'];
138
	$upcontext['updated'] = $upcontext['user']['updated'];
139
140
	$is_debug = !empty($upcontext['user']['debug']) ? true : false;
141
}
142
143
// Nothing sensible?
144
if (empty($upcontext['updated']))
145
{
146
	$upcontext['started'] = time();
147
	$upcontext['updated'] = 0;
148
	$upcontext['user'] = array(
149
		'id' => 0,
150
		'name' => 'Guest',
151
		'pass' => 0,
152
		'started' => $upcontext['started'],
153
		'updated' => $upcontext['updated'],
154
	);
155
}
156
157
// Load up some essential data...
158
loadEssentialData();
159
160
// Are we going to be mimic'ing SSI at this point?
161
if (isset($_GET['ssi']))
162
{
163
	require_once($sourcedir . '/Errors.php');
164
	require_once($sourcedir . '/Logging.php');
165
	require_once($sourcedir . '/Load.php');
166
	require_once($sourcedir . '/Security.php');
167
	require_once($sourcedir . '/Subs-Package.php');
168
169
	loadUserSettings();
170
	loadPermissions();
171
}
172
173
// Include our helper functions.
174
require_once($sourcedir . '/Subs.php');
175
require_once($sourcedir . '/LogInOut.php');
176
177
// This only exists if we're on SMF ;)
178
if (isset($modSettings['smfVersion']))
179
{
180
	$request = $smcFunc['db_query']('', '
181
		SELECT variable, value
182
		FROM {db_prefix}themes
183
		WHERE id_theme = {int:id_theme}
184
			AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
185
		array(
186
			'id_theme' => 1,
187
			'theme_url' => 'theme_url',
188
			'theme_dir' => 'theme_dir',
189
			'images_url' => 'images_url',
190
			'db_error_skip' => true,
191
		)
192
	);
193 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
194
		$modSettings[$row['variable']] = $row['value'];
195
	$smcFunc['db_free_result']($request);
196
}
197
198
if (!isset($modSettings['theme_url']))
199
{
200
	$modSettings['theme_dir'] = $boarddir . '/Themes/default';
201
	$modSettings['theme_url'] = 'Themes/default';
202
	$modSettings['images_url'] = 'Themes/default/images';
203
}
204
if (!isset($settings['default_theme_url']))
205
	$settings['default_theme_url'] = $modSettings['theme_url'];
206
if (!isset($settings['default_theme_dir']))
207
	$settings['default_theme_dir'] = $modSettings['theme_dir'];
208
209
$upcontext['is_large_forum'] = (empty($modSettings['smfVersion']) || $modSettings['smfVersion'] <= '1.1 RC1') && !empty($modSettings['totalMessages']) && $modSettings['totalMessages'] > 75000;
210
// Default title...
211
$upcontext['page_title'] = 'Updating Your SMF Installation!';
212
213
// Have we got tracking data - if so use it (It will be clean!)
214
if (isset($_GET['data']))
215
{
216
	global $is_debug;
217
218
	$upcontext['upgrade_status'] = json_decode(base64_decode($_GET['data']), true);
219
	$upcontext['current_step'] = $upcontext['upgrade_status']['curstep'];
220
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
221
	$upcontext['rid'] = $upcontext['upgrade_status']['rid'];
222
	$support_js = $upcontext['upgrade_status']['js'];
223
224
	// Only set this if the upgrader status says so.
225
	if (empty($is_debug))
226
		$is_debug = $upcontext['upgrade_status']['debug'];
227
228
	// Load the language.
229 View Code Duplication
	if (file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
230
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
231
}
232
// Set the defaults.
233
else
234
{
235
	$upcontext['current_step'] = 0;
236
	$upcontext['rid'] = mt_rand(0, 5000);
237
	$upcontext['upgrade_status'] = array(
238
		'curstep' => 0,
239
		'lang' => isset($_GET['lang']) ? $_GET['lang'] : basename($language, '.lng'),
240
		'rid' => $upcontext['rid'],
241
		'pass' => 0,
242
		'debug' => 0,
243
		'js' => 0,
244
	);
245
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
246
}
247
248
// If this isn't the first stage see whether they are logging in and resuming.
249
if ($upcontext['current_step'] != 0 || !empty($upcontext['user']['step']))
250
	checkLogin();
251
252
if ($command_line)
253
	cmdStep0();
254
255
// Don't error if we're using xml.
256
if (isset($_GET['xml']))
257
	$upcontext['return_error'] = true;
258
259
// Loop through all the steps doing each one as required.
260
$upcontext['overall_percent'] = 0;
261
foreach ($upcontext['steps'] as $num => $step)
262
{
263
	if ($num >= $upcontext['current_step'])
264
	{
265
		// The current weight of this step in terms of overall progress.
266
		$upcontext['step_weight'] = $step[3];
267
		// Make sure we reset the skip button.
268
		$upcontext['skip'] = false;
269
270
		// We cannot proceed if we're not logged in.
271
		if ($num != 0 && !$disable_security && $upcontext['user']['pass'] != $upcontext['upgrade_status']['pass'])
272
		{
273
			$upcontext['steps'][0][2]();
274
			break;
275
		}
276
277
		// Call the step and if it returns false that means pause!
278
		if (function_exists($step[2]) && $step[2]() === false)
279
			break;
280
		elseif (function_exists($step[2])) {
281
			//Start each new step with this unset, so the 'normal' template is called first
282
			unset($_GET['xml']);
283
			//Clear out warnings at the start of each step
284
			unset($upcontext['custom_warning']);
285
			$_GET['substep'] = 0;
286
			$upcontext['current_step']++;
287
		}
288
	}
289
	$upcontext['overall_percent'] += $step[3];
290
}
291
292
upgradeExit();
293
294
// Exit the upgrade script.
295
function upgradeExit($fallThrough = false)
296
{
297
	global $upcontext, $upgradeurl, $sourcedir, $command_line, $is_debug;
298
299
	// Save where we are...
300
	if (!empty($upcontext['current_step']) && !empty($upcontext['user']['id']))
301
	{
302
		$upcontext['user']['step'] = $upcontext['current_step'];
303
		$upcontext['user']['substep'] = $_GET['substep'];
304
		$upcontext['user']['updated'] = time();
305
		$upcontext['debug'] = $is_debug;
306
		$upgradeData = base64_encode(json_encode($upcontext['user']));
307
		require_once($sourcedir . '/Subs-Admin.php');
308
		updateSettingsFile(array('upgradeData' => '"' . $upgradeData . '"'));
309
		updateDbLastError(0);
0 ignored issues
show
The call to updateDbLastError() has too many arguments starting with 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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
310
	}
311
312
	// Handle the progress of the step, if any.
313
	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
314
	{
315
		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
316
		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
317
	}
318
	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
319
320
	// We usually dump our templates out.
321
	if (!$fallThrough)
322
	{
323
		// This should not happen my dear... HELP ME DEVELOPERS!!
324
		if (!empty($command_line))
325
		{
326
			if (function_exists('debug_print_backtrace'))
327
				debug_print_backtrace();
328
329
			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.';
330
			flush();
331
			die();
332
		}
333
334
		if (!isset($_GET['xml']))
335
			template_upgrade_above();
336
		else
337
		{
338
			header('Content-Type: text/xml; charset=UTF-8');
339
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
340
			$upcontext['get_data'] = array();
341
			foreach ($_GET as $k => $v)
342
			{
343
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
344
				{
345
					$upcontext['get_data'][$k] = $v;
346
				}
347
			}
348
			template_xml_above();
349
		}
350
351
		// Call the template.
352
		if (isset($upcontext['sub_template']))
353
		{
354
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
355
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(json_encode($upcontext['upgrade_status']));
356
357
			// Custom stuff to pass back?
358
			if (!empty($upcontext['query_string']))
359
				$upcontext['form_url'] .= $upcontext['query_string'];
360
361
			// Call the appropriate subtemplate
362
			if (is_callable('template_' . $upcontext['sub_template']))
363
				call_user_func('template_' . $upcontext['sub_template']);
364
			else
365
				die('Upgrade aborted!  Invalid template: template_' . $upcontext['sub_template']);
366
		}
367
368
		// Was there an error?
369
		if (!empty($upcontext['forced_error_message']))
370
			echo $upcontext['forced_error_message'];
371
372
		// Show the footer.
373
		if (!isset($_GET['xml']))
374
			template_upgrade_below();
375
		else
376
			template_xml_below();
377
	}
378
379
380
	if (!empty($command_line) && $is_debug)
381
	{
382
		$active = time() - $upcontext['started'];
383
		$hours = floor($active / 3600);
384
		$minutes = intval(($active / 60) % 60);
385
		$seconds = intval($active % 60);
386
387
		$totalTime = '';
388
		if ($hours > 0)
389
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
390
		if ($minutes > 0)
391
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
392
		if ($seconds > 0)
393
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
394
395
		if (!empty($totalTime))
396
			echo "\n" . 'Upgrade completed in ' . $totalTime . "\n";
397
	}
398
399
	// Bang - gone!
400
	die();
401
}
402
403
// Used to direct the user to another location.
404
function redirectLocation($location, $addForm = true)
405
{
406
	global $upgradeurl, $upcontext, $command_line;
407
408
	// Command line users can't be redirected.
409
	if ($command_line)
410
		upgradeExit(true);
411
412
	// Are we providing the core info?
413
	if ($addForm)
414
	{
415
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
416
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
417
	}
418
419
	while (@ob_end_clean());
420
	header('Location: ' . strtr($location, array('&amp;' => '&')));
421
422
	// Exit - saving status as we go.
423
	upgradeExit(true);
424
}
425
426
// Load all essential data and connect to the DB as this is pre SSI.php
427
function loadEssentialData()
428
{
429
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection, $db_prefix, $db_character_set, $db_type;
430
	global $modSettings, $sourcedir, $smcFunc;
431
432
	error_reporting(E_ALL);
433
	define('SMF', 1);
434
435
	// Start the session.
436
	if (@ini_get('session.save_handler') == 'user')
437
		@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 here. This can introduce security issues, and is generally not recommended.

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...
438
	@session_start();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
439
440
	if (empty($smcFunc))
441
		$smcFunc = array();
442
443
	// We need this for authentication and some upgrade code
444
	require_once($sourcedir . '/Subs-Auth.php');
445
	require_once($sourcedir . '/Class-Package.php');
446
447
	$smcFunc['strtolower'] = 'smf_strtolower';
448
449
	// Initialize everything...
450
	initialize_inputs();
451
452
	// Get the database going!
453
	if (empty($db_type) || $db_type == 'mysqli')
454
		$db_type = 'mysql';
455
456
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
457
	{
458
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
459
460
		// Make the connection...
461
		if (empty($db_connection))
462
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, array('non_fatal' => true));
463
		else
464
			// If we've returned here, ping/reconnect to be safe
465
			$smcFunc['db_ping']($db_connection);
466
467
		// Oh dear god!!
468
		if ($db_connection === null)
469
			die('Unable to connect to database - please check username and password are correct in Settings.php');
470
471
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
472
			$smcFunc['db_query']('', '
473
			SET NAMES {string:db_character_set}',
474
			array(
475
				'db_error_skip' => true,
476
				'db_character_set' => $db_character_set,
477
			)
478
		);
479
480
		// Load the modSettings data...
481
		$request = $smcFunc['db_query']('', '
482
			SELECT variable, value
483
			FROM {db_prefix}settings',
484
			array(
485
				'db_error_skip' => true,
486
			)
487
		);
488
		$modSettings = array();
489 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
490
			$modSettings[$row['variable']] = $row['value'];
491
		$smcFunc['db_free_result']($request);
492
	}
493
	else
494
	{
495
		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.');
496
	}
497
498
	require_once($sourcedir . '/Subs.php');
499
500
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
501
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
502
	{
503
		require_once($sourcedir . '/QueryString.php');
504
		cleanRequest();
505
	}
506
507
	if (!isset($_GET['substep']))
508
		$_GET['substep'] = 0;
509
}
510
511
function initialize_inputs()
0 ignored issues
show
The function initialize_inputs() has been defined more than once; this definition is ignored, only the first definition in other/install.php (L145-257) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
512
{
513
	global $start_time, $db_type;
514
515
	$start_time = time();
516
517
	umask(0);
518
519
	ob_start();
520
521
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
522
	ignore_user_abort(true);
523
524
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
525
	if (isset($_GET['delete']))
526
	{
527
		@unlink(__FILE__);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
528
529
		// And the extra little files ;).
530
		@unlink(dirname(__FILE__) . '/upgrade_1-0.sql');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
531
		@unlink(dirname(__FILE__) . '/upgrade_1-1.sql');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
532
		@unlink(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
533
		@unlink(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
534
		@unlink(dirname(__FILE__) . '/upgrade-helper.php');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
535
536
		$dh = opendir(dirname(__FILE__));
537
		while ($file = readdir($dh))
538
		{
539
			if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
540
				@unlink(dirname(__FILE__) . '/' . $file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
541
		}
542
		closedir($dh);
543
544
		// Legacy files while we're at it. NOTE: We only touch files we KNOW shouldn't be there.
545
		// 1.1 Sources files not in 2.0+
546
		@unlink(dirname(__FILE__) . '/Sources/ModSettings.php');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
547
		// 1.1 Templates that don't exist any more (e.g. renamed)
548
		@unlink(dirname(__FILE__) . '/Themes/default/Combat.template.php');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
549
		@unlink(dirname(__FILE__) . '/Themes/default/Modlog.template.php');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
550
		// 1.1 JS files were stored in the main theme folder, but in 2.0+ are in the scripts/ folder
551
		@unlink(dirname(__FILE__) . '/Themes/default/fader.js');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
552
		@unlink(dirname(__FILE__) . '/Themes/default/script.js');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
553
		@unlink(dirname(__FILE__) . '/Themes/default/spellcheck.js');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
554
		@unlink(dirname(__FILE__) . '/Themes/default/xml_board.js');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
555
		@unlink(dirname(__FILE__) . '/Themes/default/xml_topic.js');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
556
557
		// 2.0 Sources files not in 2.1+
558
		@unlink(dirname(__FILE__) . '/Sources/DumpDatabase.php');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
559
		@unlink(dirname(__FILE__) . '/Sources/LockTopic.php');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
560
561
		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');
562
		exit;
563
	}
564
565
	// Something is causing this to happen, and it's annoying.  Stop it.
566
	$temp = 'upgrade_php?step';
567
	while (strlen($temp) > 4)
568
	{
569
		if (isset($_GET[$temp]))
570
			unset($_GET[$temp]);
571
		$temp = substr($temp, 1);
572
	}
573
574
	// Force a step, defaulting to 0.
575
	$_GET['step'] = (int) @$_GET['step'];
576
	$_GET['substep'] = (int) @$_GET['substep'];
577
}
578
579
// Step 0 - Let's welcome them in and ask them to login!
580
function WelcomeLogin()
581
{
582
	global $boarddir, $sourcedir, $modSettings, $cachedir, $upgradeurl, $upcontext;
583
	global $smcFunc, $db_type, $databases, $boardurl;
584
585
	// We global $txt here so that the language files can add to them. This variable is NOT unused.
586
	global $txt;
587
588
	$upcontext['sub_template'] = 'welcome_message';
589
590
	// Check for some key files - one template, one language, and a new and an old source file.
591
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
592
		&& @file_exists($sourcedir . '/QueryString.php')
593
		&& @file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')
594
		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
595
596
	// Need legacy scripts?
597 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.1)
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
598
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
599 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.0)
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
600
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-1.sql');
601 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 1.1)
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
602
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
603
604
	// We don't need "-utf8" files anymore...
605
	$upcontext['language'] = str_ireplace('-utf8', '', $upcontext['language']);
606
607
	// This needs to exist!
608
	if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
609
		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>]');
610
	else
611
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
612
613
	if (!$check)
614
		// 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.
615
		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.');
616
617
	// Do they meet the install requirements?
618
	if (!php_version_check())
619
		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.');
620
621
	if (!db_version_check())
622
		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.');
623
624
	// Do some checks to make sure they have proper privileges
625
	db_extend('packages');
626
627
	// CREATE
628
	$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');
629
630
	// ALTER
631
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
632
633
	// DROP
634
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
635
636
	// Sorry... we need CREATE, ALTER and DROP
637 View Code Duplication
	if (!$create || !$alter || !$drop)
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
638
		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.');
639
640
	// Do a quick version spot check.
641
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
642
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
643
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
644
		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.');
645
646
	// What absolutely needs to be writable?
647
	$writable_files = array(
648
		$boarddir . '/Settings.php',
649
		$boarddir . '/Settings_bak.php',
650
		$boarddir . '/db_last_error.php',
651
		$modSettings['theme_dir'] . '/css/minified.css',
652
		$modSettings['theme_dir'] . '/scripts/minified.js',
653
		$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
654
	);
655
656
	// Do we need to add this setting?
657
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
658
659
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] . '/custom_avatar';
660
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
661
662
	// This little fellow has to cooperate...
663
	quickFileWritable($custom_av_dir);
664
665
	// Are we good now?
666
	if (!is_writable($custom_av_dir))
667
		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));
668
	elseif ($need_settings_update)
669
	{
670
		if (!function_exists('cache_put_data'))
671
			require_once($sourcedir . '/Load.php');
672
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
673
		updateSettings(array('custom_avatar_url' => $custom_av_url));
674
	}
675
676
	require_once($sourcedir . '/Security.php');
677
678
	// Check the cache directory.
679
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
680
	if (!file_exists($cachedir_temp))
681
		@mkdir($cachedir_temp);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
682
	if (!file_exists($cachedir_temp))
683
		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.');
684
685
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
686
		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>.');
687
	elseif (!isset($_GET['skiplang']))
688
	{
689
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
690
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
691
692
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
693
			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>]');
694
	}
695
696
	if (!makeFilesWritable($writable_files))
697
		return false;
698
699
	// Check agreement.txt. (it may not exist, in which case $boarddir must be writable.)
700 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
701
		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.');
702
703
	// Upgrade the agreement.
704
	elseif (isset($modSettings['agreement']))
705
	{
706
		$fp = fopen($boarddir . '/agreement.txt', 'w');
707
		fwrite($fp, $modSettings['agreement']);
708
		fclose($fp);
709
	}
710
711
	// We're going to check that their board dir setting is right in case they've been moving stuff around.
712
	if (strtr($boarddir, array('/' => '', '\\' => '')) != strtr(dirname(__FILE__), array('/' => '', '\\' => '')))
713
		$upcontext['warning'] = '
714
			It looks as if your board directory settings <em>might</em> be incorrect. Your board directory is currently set to &quot;' . $boarddir . '&quot; but should probably be &quot;' . dirname(__FILE__) . '&quot;. Settings.php currently lists your paths as:<br>
715
			<ul>
716
				<li>Board Directory: ' . $boarddir . '</li>
717
				<li>Source Directory: ' . $boarddir . '</li>
718
				<li>Cache Directory: ' . $cachedir_temp . '</li>
719
			</ul>
720
			If these seem incorrect please open Settings.php in a text editor before proceeding with this upgrade. If they are incorrect due to you moving your forum to a new location please download and execute the <a href="https://download.simplemachines.org/?tools">Repair Settings</a> tool from the Simple Machines website before continuing.';
721
722
	// Confirm mbstring is loaded...
723
	if (!extension_loaded('mbstring'))
724
		return throw_error($txt['install_no_mbstring']);
725
726
	// Check for https stream support.
727
	$supported_streams = stream_get_wrappers();
728
	if (!in_array('https', $supported_streams))
729
		$upcontext['custom_warning'] = $txt['install_no_https'];
730
731
	// Either we're logged in or we're going to present the login.
732
	if (checkLogin())
733
		return true;
734
735
	$upcontext += createToken('login');
736
737
	return false;
738
}
739
740
// Step 0.5: Does the login work?
741
function checkLogin()
742
{
743
	global $modSettings, $upcontext, $disable_security;
744
	global $smcFunc, $db_type, $support_js;
745
746
	// Don't bother if the security is disabled.
747
	if ($disable_security)
748
		return true;
749
750
	// Are we trying to login?
751
	if (isset($_POST['contbutt']) && (!empty($_POST['user'])))
752
	{
753
		// If we've disabled security pick a suitable name!
754
		if (empty($_POST['user']))
755
			$_POST['user'] = 'Administrator';
756
757
		// Before 2.0 these column names were different!
758
		$oldDB = false;
759
		if (empty($db_type) || $db_type == 'mysql')
760
		{
761
			$request = $smcFunc['db_query']('', '
762
				SHOW COLUMNS
763
				FROM {db_prefix}members
764
				LIKE {string:member_name}',
765
				array(
766
					'member_name' => 'memberName',
767
					'db_error_skip' => true,
768
				)
769
			);
770
			if ($smcFunc['db_num_rows']($request) != 0)
771
				$oldDB = true;
772
			$smcFunc['db_free_result']($request);
773
		}
774
775
		// Get what we believe to be their details.
776
		if (!$disable_security)
777
		{
778
			if ($oldDB)
779
				$request = $smcFunc['db_query']('', '
780
					SELECT id_member, memberName AS member_name, passwd, id_group,
781
					additionalGroups AS additional_groups, lngfile
782
					FROM {db_prefix}members
783
					WHERE memberName = {string:member_name}',
784
					array(
785
						'member_name' => $_POST['user'],
786
						'db_error_skip' => true,
787
					)
788
				);
789
			else
790
				$request = $smcFunc['db_query']('', '
791
					SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile
792
					FROM {db_prefix}members
793
					WHERE member_name = {string:member_name}',
794
					array(
795
						'member_name' => $_POST['user'],
796
						'db_error_skip' => true,
797
					)
798
				);
799
			if ($smcFunc['db_num_rows']($request) != 0)
800
			{
801
				list ($id_member, $name, $password, $id_group, $addGroups, $user_language) = $smcFunc['db_fetch_row']($request);
802
803
				$groups = explode(',', $addGroups);
804
				$groups[] = $id_group;
805
806
				foreach ($groups as $k => $v)
807
					$groups[$k] = (int) $v;
808
809
				$sha_passwd = sha1(strtolower($name) . un_htmlspecialchars($_REQUEST['passwrd']));
810
811
				// We don't use "-utf8" anymore...
812
				$user_language = str_ireplace('-utf8', '', $user_language);
813
			}
814
			else
815
				$upcontext['username_incorrect'] = true;
816
			$smcFunc['db_free_result']($request);
817
		}
818
		$upcontext['username'] = $_POST['user'];
819
820
		// Track whether javascript works!
821
		if (!empty($_POST['js_works']))
822
		{
823
			$upcontext['upgrade_status']['js'] = 1;
824
			$support_js = 1;
825
		}
826
		else
827
			$support_js = 0;
828
829
		// Note down the version we are coming from.
830
		if (!empty($modSettings['smfVersion']) && empty($upcontext['user']['version']))
831
			$upcontext['user']['version'] = $modSettings['smfVersion'];
832
833
		// Didn't get anywhere?
834
		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']))
0 ignored issues
show
The variable $sha_passwd does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
835
		{
836
			// MD5?
837
			$md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user']));
838
			if ($md5pass != $password)
0 ignored issues
show
The variable $password does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
839
			{
840
				$upcontext['password_failed'] = true;
841
				// Disable the hashing this time.
842
				$upcontext['disable_login_hashing'] = true;
843
			}
844
		}
845
846
		if ((empty($upcontext['password_failed']) && !empty($name)) || $disable_security)
847
		{
848
			// Set the password.
849
			if (!$disable_security)
850
			{
851
				// Do we actually have permission?
852
				if (!in_array(1, $groups))
853
				{
854
					$request = $smcFunc['db_query']('', '
855
						SELECT permission
856
						FROM {db_prefix}permissions
857
						WHERE id_group IN ({array_int:groups})
858
							AND permission = {string:admin_forum}',
859
						array(
860
							'groups' => $groups,
0 ignored issues
show
The variable $groups does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
861
							'admin_forum' => 'admin_forum',
862
							'db_error_skip' => true,
863
						)
864
					);
865
					if ($smcFunc['db_num_rows']($request) == 0)
866
						return throw_error('You need to be an admin to perform an upgrade!');
867
					$smcFunc['db_free_result']($request);
868
				}
869
870
				$upcontext['user']['id'] = $id_member;
0 ignored issues
show
The variable $id_member does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
871
				$upcontext['user']['name'] = $name;
872
			}
873
			else
874
			{
875
				$upcontext['user']['id'] = 1;
876
				$upcontext['user']['name'] = 'Administrator';
877
			}
878
			$upcontext['user']['pass'] = mt_rand(0, 60000);
879
			// This basically is used to match the GET variables to Settings.php.
880
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
881
882
			// Set the language to that of the user?
883
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
884
			{
885
				$user_language = basename($user_language, '.lng');
886
				$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
887
				preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
888
889
				if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
890
					$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'] . '.';
891
				elseif (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . basename($user_language, '.lng') . '.php'))
892
					$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'] . '.';
893
				else
894
				{
895
					// Set this as the new language.
896
					$upcontext['language'] = $user_language;
897
					$upcontext['upgrade_status']['lang'] = $upcontext['language'];
898
899
					// Include the file.
900
					require_once($modSettings['theme_dir'] . '/languages/Install.' . $user_language . '.php');
901
				}
902
			}
903
904
			// If we're resuming set the step and substep to be correct.
905
			if (isset($_POST['cont']))
906
			{
907
				$upcontext['current_step'] = $upcontext['user']['step'];
908
				$_GET['substep'] = $upcontext['user']['substep'];
909
			}
910
911
			return true;
912
		}
913
	}
914
915
	return false;
916
}
917
918
// Step 1: Do the maintenance and backup.
919
function UpgradeOptions()
920
{
921
	global $db_prefix, $command_line, $modSettings, $is_debug, $smcFunc, $packagesdir, $tasksdir, $language;
922
	global $boarddir, $boardurl, $sourcedir, $maintenance, $cachedir, $upcontext, $db_type, $db_server;
923
924
	$upcontext['sub_template'] = 'upgrade_options';
925
	$upcontext['page_title'] = 'Upgrade Options';
926
927
	db_extend('packages');
928
	$upcontext['karma_installed'] = array('good' => false, 'bad' => false);
929
	$member_columns = $smcFunc['db_list_columns']('{db_prefix}members');
930
931
	$upcontext['karma_installed']['good'] = in_array('karma_good', $member_columns);
932
	$upcontext['karma_installed']['bad'] = in_array('karma_bad', $member_columns);
933
934
	unset($member_columns);
935
936
	// If we've not submitted then we're done.
937
	if (empty($_POST['upcont']))
938
		return false;
939
940
	// Firstly, if they're enabling SM stat collection just do it.
941
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']))
942
	{
943
		$upcontext['allow_sm_stats'] = true;
944
945
		// Don't register if we still have a key.
946
		if (empty($modSettings['sm_stats_key']))
947
		{
948
			// Attempt to register the site etc.
949
			$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
950 View Code Duplication
			if ($fp)
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
951
			{
952
				$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
953
				$out .= 'Host: www.simplemachines.org' . "\r\n";
954
				$out .= 'Connection: Close' . "\r\n\r\n";
955
				fwrite($fp, $out);
956
957
				$return_data = '';
958
				while (!feof($fp))
959
					$return_data .= fgets($fp, 128);
960
961
				fclose($fp);
962
963
				// Get the unique site ID.
964
				preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);
965
966
				if (!empty($ID[1]))
967
					$smcFunc['db_insert']('replace',
968
						$db_prefix . 'settings',
969
						array('variable' => 'string', 'value' => 'string'),
970
						array(
971
							array('sm_stats_key', $ID[1]),
972
							array('enable_sm_stats', 1),
973
						),
974
						array('variable')
975
					);
976
			}
977
		}
978
		else
979
		{
980
			$smcFunc['db_insert']('replace',
981
				$db_prefix . 'settings',
982
				array('variable' => 'string', 'value' => 'string'),
983
				array('enable_sm_stats', 1),
984
				array('variable')
985
			);
986
		}
987
	}
988
	// Don't remove stat collection unless we unchecked the box for real, not from the loop.
989 View Code Duplication
	elseif (empty($_POST['stats']) && empty($upcontext['allow_sm_stats']))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
990
		$smcFunc['db_query']('', '
991
			DELETE FROM {db_prefix}settings
992
			WHERE variable = {string:enable_sm_stats}',
993
			array(
994
				'enable_sm_stats' => 'enable_sm_stats',
995
				'db_error_skip' => true,
996
			)
997
		);
998
999
	// Deleting old karma stuff?
1000
	if (!empty($_POST['delete_karma']))
1001
	{
1002
		// Delete old settings vars.
1003
		$smcFunc['db_query']('', '
1004
			DELETE FROM {db_prefix}settings
1005
			WHERE variable IN ({array_string:karma_vars})',
1006
			array(
1007
				'karma_vars' => array('karmaMode', 'karmaTimeRestrictAdmins', 'karmaWaitTime', 'karmaMinPosts', 'karmaLabel', 'karmaSmiteLabel', 'karmaApplaudLabel'),
1008
			)
1009
		);
1010
1011
		// Cleaning up old karma member settings.
1012
		if ($upcontext['karma_installed']['good'])
1013
			$smcFunc['db_query']('', '
1014
				ALTER TABLE {db_prefix}members
1015
				DROP karma_good',
1016
				array()
1017
			);
1018
1019
		// Does karma bad was enable?
1020
		if ($upcontext['karma_installed']['bad'])
1021
			$smcFunc['db_query']('', '
1022
				ALTER TABLE {db_prefix}members
1023
				DROP karma_bad',
1024
				array()
1025
			);
1026
1027
		// Cleaning up old karma permissions.
1028
		$smcFunc['db_query']('', '
1029
			DELETE FROM {db_prefix}permissions
1030
			WHERE permission = {string:karma_vars}',
1031
			array(
1032
				'karma_vars' => 'karma_edit',
1033
			)
1034
		);
1035
	}
1036
1037
	// Emptying the error log?
1038
	if (!empty($_POST['empty_error']))
1039
		$smcFunc['db_query']('truncate_table', '
1040
			TRUNCATE {db_prefix}log_errors',
1041
			array(
1042
			)
1043
		);
1044
1045
	$changes = array();
1046
1047
	// Add proxy settings.
1048
	if (!isset($GLOBALS['image_proxy_maxsize']))
1049
		$changes += array(
1050
			'image_proxy_secret' => '\'' . substr(sha1(mt_rand()), 0, 20) . '\'',
1051
			'image_proxy_maxsize' => 5190,
1052
			'image_proxy_enabled' => 0,
1053
		);
1054
1055
	// If we're overriding the language follow it through.
1056 View Code Duplication
	if (isset($_GET['lang']) && file_exists($modSettings['theme_dir'] . '/languages/index.' . $_GET['lang'] . '.php'))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1057
		$changes['language'] = '\'' . $_GET['lang'] . '\'';
1058
1059
	if (!empty($_POST['maint']))
1060
	{
1061
		$changes['maintenance'] = '2';
1062
		// Remember what it was...
1063
		$upcontext['user']['main'] = $maintenance;
1064
1065
		if (!empty($_POST['maintitle']))
1066
		{
1067
			$changes['mtitle'] = '\'' . addslashes($_POST['maintitle']) . '\'';
1068
			$changes['mmessage'] = '\'' . addslashes($_POST['mainmessage']) . '\'';
1069
		}
1070
		else
1071
		{
1072
			$changes['mtitle'] = '\'Upgrading the forum...\'';
1073
			$changes['mmessage'] = '\'Don\\\'t worry, we will be back shortly with an updated forum.  It will only be a minute ;).\'';
1074
		}
1075
	}
1076
1077
	if ($command_line)
1078
		echo ' * Updating Settings.php...';
1079
1080
	// Fix some old paths.
1081 View Code Duplication
	if (substr($boarddir, 0, 1) == '.')
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1082
		$changes['boarddir'] = '\'' . fixRelativePath($boarddir) . '\'';
1083
1084 View Code Duplication
	if (substr($sourcedir, 0, 1) == '.')
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1085
		$changes['sourcedir'] = '\'' . fixRelativePath($sourcedir) . '\'';
1086
1087
	if (empty($cachedir) || substr($cachedir, 0, 1) == '.')
1088
		$changes['cachedir'] = '\'' . fixRelativePath($boarddir) . '/cache\'';
1089
1090
	// Not had the database type added before?
1091
	if (empty($db_type))
1092
		$changes['db_type'] = 'mysql';
1093
1094
	// If they have a "host:port" setup for the host, split that into separate values
1095
	// You should never have a : in the hostname if you're not on MySQL, but better safe than sorry
1096
	if (strpos($db_server, ':') !== false && $db_type == 'mysql')
1097
	{
1098
		list ($db_server, $db_port) = explode(':', $db_server);
1099
1100
		$changes['db_server'] = '\'' . $db_server . '\'';
1101
1102
		// Only set this if we're not using the default port
1103
		if ($db_port != ini_get('mysqli.default_port'))
1104
			$changes['db_port'] = (int) $db_port;
1105
	}
1106
	elseif (!empty($db_port))
0 ignored issues
show
The variable $db_port seems only to be defined at a later point. As such the call to empty() seems to always evaluate to true.

This check marks calls to isset(...) or empty(...) that are found before the variable itself is defined. These will always have the same result.

This is likely the result of code being shifted around. Consider removing these calls.

Loading history...
1107
	{
1108
		// If db_port is set and is the same as the default, set it to ''
1109
		if ($db_type == 'mysql')
1110
		{
1111
			if ($db_port == ini_get('mysqli.default_port'))
1112
				$changes['db_port'] = '\'\'';
1113
			elseif ($db_type == 'postgresql' && $db_port == 5432)
1114
				$changes['db_port'] = '\'\'';
1115
		}
1116
	}
1117
1118
	// Maybe we haven't had this option yet?
1119
	if (empty($packagesdir))
1120
		$changes['packagesdir'] = '\'' . fixRelativePath($boarddir) . '/Packages\'';
1121
1122
	// Add support for $tasksdir var.
1123
	if (empty($tasksdir))
1124
		$changes['tasksdir'] = '\'' . fixRelativePath($sourcedir) . '/tasks\'';
1125
1126
	// Make sure we fix the language as well.
1127
	if (stristr($language, '-utf8'))
1128
		$changes['language'] = '\'' . str_ireplace('-utf8', '', $language) . '\'';
1129
1130
	// @todo Maybe change the cookie name if going to 1.1, too?
1131
1132
	// Update Settings.php with the new settings.
1133
	require_once($sourcedir . '/Subs-Admin.php');
1134
	updateSettingsFile($changes);
1135
1136
	if ($command_line)
1137
		echo ' Successful.' . "\n";
1138
1139
	// Are we doing debug?
1140
	if (isset($_POST['debug']))
1141
	{
1142
		$upcontext['upgrade_status']['debug'] = true;
1143
		$is_debug = true;
1144
	}
1145
1146
	// If we're not backing up then jump one.
1147
	if (empty($_POST['backup']))
1148
		$upcontext['current_step']++;
1149
1150
	// If we've got here then let's proceed to the next step!
1151
	return true;
1152
}
1153
1154
// Backup the database - why not...
1155
function BackupDatabase()
1156
{
1157
	global $upcontext, $db_prefix, $command_line, $support_js, $file_steps, $smcFunc;
1158
1159
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1160
	$upcontext['page_title'] = 'Backup Database';
1161
1162
	// Done it already - js wise?
1163
	if (!empty($_POST['backup_done']))
1164
		return true;
1165
1166
	// Some useful stuff here.
1167
	db_extend();
1168
1169
	// Might need this as well
1170
	db_extend('packages');
1171
1172
	// Get all the table names.
1173
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1174
	$db = preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? strtr($match[1], array('`' => '')) : false;
1175
	$tables = $smcFunc['db_list_tables']($db, $filter);
1176
1177
	$table_names = array();
1178
	foreach ($tables as $table)
1179
		if (substr($table, 0, 7) !== 'backup_')
1180
			$table_names[] = $table;
1181
1182
	$upcontext['table_count'] = count($table_names);
1183
	$upcontext['cur_table_num'] = $_GET['substep'];
1184
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1185
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1186
	// For non-java auto submit...
1187
	$file_steps = $upcontext['table_count'];
1188
1189
	// What ones have we already done?
1190 View Code Duplication
	foreach ($table_names as $id => $table)
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1191
		if ($id < $_GET['substep'])
1192
			$upcontext['previous_tables'][] = $table;
1193
1194
	if ($command_line)
1195
		echo 'Backing Up Tables.';
1196
1197
	// If we don't support javascript we backup here.
1198
	if (!$support_js || isset($_GET['xml']))
1199
	{
1200
		// Backup each table!
1201
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1202
		{
1203
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1204
			$upcontext['cur_table_num'] = $substep + 1;
1205
1206
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1207
1208
			// Do we need to pause?
1209
			nextSubstep($substep);
1210
1211
			backupTable($table_names[$substep]);
1212
1213
			// If this is XML to keep it nice for the user do one table at a time anyway!
1214
			if (isset($_GET['xml']))
1215
				return upgradeExit();
1216
		}
1217
1218
		if ($command_line)
1219
		{
1220
			echo "\n" . ' Successful.\'' . "\n";
1221
			flush();
1222
		}
1223
		$upcontext['step_progress'] = 100;
1224
1225
		$_GET['substep'] = 0;
1226
		// Make sure we move on!
1227
		return true;
1228
	}
1229
1230
	// Either way next place to post will be database changes!
1231
	$_GET['substep'] = 0;
1232
	return false;
1233
}
1234
1235
// Backup one table...
1236
function backupTable($table)
1237
{
1238
	global $command_line, $db_prefix, $smcFunc;
1239
1240
	if ($command_line)
1241
	{
1242
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1243
		flush();
1244
	}
1245
1246
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1247
1248
	if ($command_line)
1249
		echo ' done.';
1250
}
1251
1252
// Step 2: Everything.
1253
function DatabaseChanges()
1254
{
1255
	global $db_prefix, $modSettings, $smcFunc;
1256
	global $upcontext, $support_js, $db_type;
1257
1258
	// Have we just completed this?
1259
	if (!empty($_POST['database_done']))
1260
		return true;
1261
1262
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1263
	$upcontext['page_title'] = 'Database Changes';
1264
1265
	// All possible files.
1266
	// Name, < version, insert_on_complete
1267
	$files = array(
1268
		array('upgrade_1-0.sql', '1.1', '1.1 RC0'),
1269
		array('upgrade_1-1.sql', '2.0', '2.0 a'),
1270
		array('upgrade_2-0_' . $db_type . '.sql', '2.1', '2.1 dev0'),
1271
		array('upgrade_2-1_' . $db_type . '.sql', '3.0', SMF_VERSION),
1272
	);
1273
1274
	// How many files are there in total?
1275
	if (isset($_GET['filecount']))
1276
		$upcontext['file_count'] = (int) $_GET['filecount'];
1277
	else
1278
	{
1279
		$upcontext['file_count'] = 0;
1280
		foreach ($files as $file)
1281
		{
1282
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1283
				$upcontext['file_count']++;
1284
		}
1285
	}
1286
1287
	// Do each file!
1288
	$did_not_do = count($files) - $upcontext['file_count'];
1289
	$upcontext['step_progress'] = 0;
1290
	$upcontext['cur_file_num'] = 0;
1291
	foreach ($files as $file)
1292
	{
1293
		if ($did_not_do)
1294
			$did_not_do--;
1295
		else
1296
		{
1297
			$upcontext['cur_file_num']++;
1298
			$upcontext['cur_file_name'] = $file[0];
1299
			// Do we actually need to do this still?
1300
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1301
			{
1302
				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1303
				if ($nextFile)
1304
				{
1305
					// Only update the version of this if complete.
1306
					$smcFunc['db_insert']('replace',
1307
						$db_prefix . 'settings',
1308
						array('variable' => 'string', 'value' => 'string'),
1309
						array('smfVersion', $file[2]),
1310
						array('variable')
1311
					);
1312
1313
					$modSettings['smfVersion'] = $file[2];
1314
				}
1315
1316
				// If this is XML we only do this stuff once.
1317
				if (isset($_GET['xml']))
1318
				{
1319
					// Flag to move on to the next.
1320
					$upcontext['completed_step'] = true;
1321
					// Did we complete the whole file?
1322
					if ($nextFile)
1323
						$upcontext['current_debug_item_num'] = -1;
1324
					return upgradeExit();
1325
				}
1326
				elseif ($support_js)
1327
					break;
1328
			}
1329
			// Set the progress bar to be right as if we had - even if we hadn't...
1330
			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1331
		}
1332
	}
1333
1334
	$_GET['substep'] = 0;
1335
	// So the template knows we're done.
1336
	if (!$support_js)
1337
	{
1338
		$upcontext['changes_complete'] = true;
1339
1340
		return true;
1341
	}
1342
	return false;
1343
}
1344
1345
1346
// Delete the damn thing!
1347
function DeleteUpgrade()
1348
{
1349
	global $command_line, $language, $upcontext, $sourcedir, $forum_version, $user_info, $maintenance, $smcFunc, $db_type;
1350
1351
	// Now it's nice to have some of the basic SMF source files.
1352
	if (!isset($_GET['ssi']) && !$command_line)
1353
		redirectLocation('&ssi=1');
1354
1355
	$upcontext['sub_template'] = 'upgrade_complete';
1356
	$upcontext['page_title'] = 'Upgrade Complete';
1357
1358
	$endl = $command_line ? "\n" : '<br>' . "\n";
1359
1360
	$changes = array(
1361
		'language' => '\'' . (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language) . '\'',
1362
		'db_error_send' => '1',
1363
		'upgradeData' => '\'\'',
1364
	);
1365
1366
	// Are we in maintenance mode?
1367
	if (isset($upcontext['user']['main']))
1368
	{
1369
		if ($command_line)
1370
			echo ' * ';
1371
		$upcontext['removed_maintenance'] = true;
1372
		$changes['maintenance'] = $upcontext['user']['main'];
1373
	}
1374
	// Otherwise if somehow we are in 2 let's go to 1.
1375
	elseif (!empty($maintenance) && $maintenance == 2)
1376
		$changes['maintenance'] = 1;
1377
1378
	// Wipe this out...
1379
	$upcontext['user'] = array();
1380
1381
	require_once($sourcedir . '/Subs-Admin.php');
1382
	updateSettingsFile($changes);
1383
1384
	// Clean any old cache files away.
1385
	upgrade_clean_cache();
1386
1387
	// Can we delete the file?
1388
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1389
1390
	// Now is the perfect time to fetch the SM files.
1391
	if ($command_line)
1392
		cli_scheduled_fetchSMfiles();
1393
	else
1394
	{
1395
		require_once($sourcedir . '/ScheduledTasks.php');
1396
		$forum_version = SMF_VERSION; // The variable is usually defined in index.php so lets just use the constant to do it for us.
1397
		scheduled_fetchSMfiles(); // Now go get those files!
1398
	}
1399
1400
	// Log what we've done.
1401
	if (empty($user_info['id']))
1402
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
1403
1404
	// Log the action manually, so CLI still works.
1405
	$smcFunc['db_insert']('',
1406
		'{db_prefix}log_actions',
1407
		array(
1408
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'inet', 'action' => 'string',
1409
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
1410
		),
1411
		array(
1412
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
1413
			0, 0, 0, json_encode(array('version' => $forum_version, 'member' => $user_info['id'])),
1414
		),
1415
		array('id_action')
1416
	);
1417
	$user_info['id'] = 0;
1418
1419
	// Save the current database version.
1420
	$server_version = $smcFunc['db_server_info']();
1421 View Code Duplication
	if ($db_type == 'mysql' && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51')))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1422
		updateSettings(array('db_mysql_group_by_fix' => '1'));
1423
1424
	if ($command_line)
1425
	{
1426
		echo $endl;
1427
		echo 'Upgrade Complete!', $endl;
1428
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
1429
		exit;
1430
	}
1431
1432
	// Make sure it says we're done.
1433
	$upcontext['overall_percent'] = 100;
1434
	if (isset($upcontext['step_progress']))
1435
		unset($upcontext['step_progress']);
1436
1437
	$_GET['substep'] = 0;
1438
	return false;
1439
}
1440
1441
// Just like the built in one, but setup for CLI to not use themes.
1442
function cli_scheduled_fetchSMfiles()
1443
{
1444
	global $sourcedir, $language, $forum_version, $modSettings, $smcFunc;
1445
1446
	if (empty($modSettings['time_format']))
1447
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1448
1449
	// What files do we want to get
1450
	$request = $smcFunc['db_query']('', '
1451
		SELECT id_file, filename, path, parameters
1452
		FROM {db_prefix}admin_info_files',
1453
		array(
1454
		)
1455
	);
1456
1457
	$js_files = array();
1458 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1459
	{
1460
		$js_files[$row['id_file']] = array(
1461
			'filename' => $row['filename'],
1462
			'path' => $row['path'],
1463
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode($forum_version)),
1464
		);
1465
	}
1466
	$smcFunc['db_free_result']($request);
1467
1468
	// We're gonna need fetch_web_data() to pull this off.
1469
	require_once($sourcedir . '/Subs-Package.php');
1470
1471
	foreach ($js_files as $ID_FILE => $file)
1472
	{
1473
		// Create the url
1474
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'https://www.simplemachines.org' : '';
1475
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1476
1477
		// Get the file
1478
		$file_data = fetch_web_data($url);
1479
1480
		// If we got an error - give up - the site might be down.
1481
		if ($file_data === false)
1482
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
1483
1484
		// Save the file to the database.
1485
		$smcFunc['db_query']('substring', '
1486
			UPDATE {db_prefix}admin_info_files
1487
			SET data = SUBSTRING({string:file_data}, 1, 65534)
1488
			WHERE id_file = {int:id_file}',
1489
			array(
1490
				'id_file' => $ID_FILE,
1491
				'file_data' => $file_data,
1492
			)
1493
		);
1494
	}
1495
	return true;
1496
}
1497
1498
function convertSettingsToTheme()
1499
{
1500
	global $db_prefix, $modSettings, $smcFunc;
1501
1502
	$values = array(
1503
		'show_latest_member' => @$GLOBALS['showlatestmember'],
1504
		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
1505
		'show_modify' => @$GLOBALS['showmodify'],
1506
		'show_user_images' => @$GLOBALS['showuserpic'],
1507
		'show_blurb' => @$GLOBALS['showusertext'],
1508
		'show_gender' => @$GLOBALS['showgenderimage'],
1509
		'show_newsfader' => @$GLOBALS['shownewsfader'],
1510
		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
1511
		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
1512
		'linktree_link' => @$GLOBALS['curposlinks'],
1513
		'show_profile_buttons' => @$GLOBALS['profilebutton'],
1514
		'show_mark_read' => @$GLOBALS['showmarkread'],
1515
		'newsfader_time' => @$GLOBALS['fadertime'],
1516
		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
1517
		'enable_news' => @$GLOBALS['enable_news'],
1518
		'return_to_post' => @$modSettings['returnToPost'],
1519
	);
1520
1521
	$themeData = array();
1522
	foreach ($values as $variable => $value)
1523
	{
1524
		if (!isset($value) || $value === null)
1525
			$value = 0;
1526
1527
		$themeData[] = array(0, 1, $variable, $value);
1528
	}
1529 View Code Duplication
	if (!empty($themeData))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1530
	{
1531
		$smcFunc['db_insert']('ignore',
1532
			$db_prefix . 'themes',
1533
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
1534
			$themeData,
1535
			array('id_member', 'id_theme', 'variable')
1536
		);
1537
	}
1538
}
1539
1540
// This function only works with MySQL but that's fine as it is only used for v1.0.
1541
function convertSettingstoOptions()
1542
{
1543
	global $modSettings, $smcFunc;
1544
1545
	// Format: new_setting -> old_setting_name.
1546
	$values = array(
1547
		'calendar_start_day' => 'cal_startmonday',
1548
		'view_newest_first' => 'viewNewestFirst',
1549
		'view_newest_pm_first' => 'viewNewestFirst',
1550
	);
1551
1552
	foreach ($values as $variable => $value)
1553
	{
1554
		if (empty($modSettings[$value[0]]))
1555
			continue;
1556
1557
		$smcFunc['db_query']('', '
1558
			INSERT IGNORE INTO {db_prefix}themes
1559
				(id_member, id_theme, variable, value)
1560
			SELECT id_member, 1, {string:variable}, {string:value}
1561
			FROM {db_prefix}members',
1562
			array(
1563
				'variable' => $variable,
1564
				'value' => $modSettings[$value[0]],
1565
				'db_error_skip' => true,
1566
			)
1567
		);
1568
1569
		$smcFunc['db_query']('', '
1570
			INSERT IGNORE INTO {db_prefix}themes
1571
				(id_member, id_theme, variable, value)
1572
			VALUES (-1, 1, {string:variable}, {string:value})',
1573
			array(
1574
				'variable' => $variable,
1575
				'value' => $modSettings[$value[0]],
1576
				'db_error_skip' => true,
1577
			)
1578
		);
1579
	}
1580
}
1581
1582
function php_version_check()
1583
{
1584
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
1585
}
1586
1587
function db_version_check()
1588
{
1589
	global $db_type, $databases;
1590
1591
	$curver = eval($databases[$db_type]['version_check']);
0 ignored issues
show
The function db_version_check() contains an eval expression.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
1592
	$curver = preg_replace('~\-.+?$~', '', $curver);
1593
1594
	return version_compare($databases[$db_type]['version'], $curver, '<=');
1595
}
1596
1597
function fixRelativePath($path)
1598
{
1599
	global $install_path;
1600
1601
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
1602
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
1603
}
1604
1605
function parse_sql($filename)
1606
{
1607
	global $db_prefix, $db_collation, $boarddir, $boardurl, $command_line, $file_steps, $step_progress, $custom_warning;
1608
	global $upcontext, $support_js, $is_debug, $db_type, $db_character_set;
1609
1610
/*
1611
	Failure allowed on:
1612
		- INSERT INTO but not INSERT IGNORE INTO.
1613
		- UPDATE IGNORE but not UPDATE.
1614
		- ALTER TABLE and ALTER IGNORE TABLE.
1615
		- DROP TABLE.
1616
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
1617
1618
	If a comment...
1619
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
1620
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
1621
		- is only ---#, it is "done." and then a break - only shown in debug.
1622
		- begins with ---{ it is a code block terminating at ---}.
1623
1624
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
1625
1626
	Replaces the following variables:
1627
		- {$boarddir}
1628
		- {$boardurl}
1629
		- {$db_prefix}
1630
		- {$db_collation}
1631
*/
1632
1633
	// May want to use extended functionality.
1634
	db_extend();
1635
	db_extend('packages');
1636
1637
	// Our custom error handler - does nothing but does stop public errors from XML!
1638
	set_error_handler(
1639
		function ($errno, $errstr, $errfile, $errline) use ($support_js)
1640
		{
1641
			if ($support_js)
1642
				return true;
1643
			else
1644
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
1645
		}
1646
	);
1647
1648
	// If we're on MySQL, set {db_collation}; this approach is used throughout upgrade_2-0_mysql.php to set new tables to utf8
1649
	// Note it is expected to be in the format: ENGINE=MyISAM{$db_collation};
1650
	if ($db_type == 'mysql')
1651
		$db_collation = ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1652
	else
1653
		$db_collation = '';
1654
1655
	$endl = $command_line ? "\n" : '<br>' . "\n";
1656
1657
	$lines = file($filename);
1658
1659
	$current_type = 'sql';
1660
	$current_data = '';
1661
	$substep = 0;
1662
	$last_step = '';
1663
1664
	// Make sure all newly created tables will have the proper characters set; this approach is used throughout upgrade_2-1_mysql.php
1665
	if (isset($db_character_set) && $db_character_set === 'utf8')
1666
		$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
1667
1668
	// Count the total number of steps within this file - for progress.
1669
	$file_steps = substr_count(implode('', $lines), '---#');
1670
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
1671
	$upcontext['debug_items'] = $file_steps;
1672
	$upcontext['current_item_num'] = 0;
1673
	$upcontext['current_item_name'] = '';
1674
	$upcontext['current_debug_item_num'] = 0;
1675
	$upcontext['current_debug_item_name'] = '';
1676
	// This array keeps a record of what we've done in case java is dead...
1677
	$upcontext['actioned_items'] = array();
1678
1679
	$done_something = false;
1680
1681
	foreach ($lines as $line_number => $line)
1682
	{
1683
		$do_current = $substep >= $_GET['substep'];
1684
1685
		// Get rid of any comments in the beginning of the line...
1686
		if (substr(trim($line), 0, 2) === '/*')
1687
			$line = preg_replace('~/\*.+?\*/~', '', $line);
1688
1689
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
1690
		if ($is_debug && !$support_js && $command_line)
1691
			flush();
1692
1693
		if (trim($line) === '')
1694
			continue;
1695
1696
		if (trim(substr($line, 0, 3)) === '---')
1697
		{
1698
			$type = substr($line, 3, 1);
1699
1700
			// An error??
1701
			if (trim($current_data) != '' && $type !== '}')
1702
			{
1703
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
1704
				if ($command_line)
1705
					echo $upcontext['error_message'];
1706
			}
1707
1708
			if ($type == ' ')
1709
			{
1710
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
1711
				{
1712
					echo ' Successful.', $endl;
1713
					flush();
1714
				}
1715
1716
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
1717
				$upcontext['current_item_num']++;
1718
				$upcontext['current_item_name'] = $last_step;
1719
1720
				if ($do_current)
1721
				{
1722
					$upcontext['actioned_items'][] = $last_step;
1723
					if ($command_line)
1724
						echo ' * ';
1725
				}
1726
			}
1727
			elseif ($type == '#')
1728
			{
1729
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
1730
1731
				$upcontext['current_debug_item_num']++;
1732
				if (trim($line) != '---#')
1733
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
1734
1735
				// Have we already done something?
1736
				if (isset($_GET['xml']) && $done_something)
1737
				{
1738
					restore_error_handler();
1739
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
1740
				}
1741
1742
				if ($do_current)
1743
				{
1744
					if (trim($line) == '---#' && $command_line)
1745
						echo ' done.', $endl;
1746
					elseif ($command_line)
1747
						echo ' +++ ', rtrim(substr($line, 4));
1748
					elseif (trim($line) != '---#')
1749
					{
1750
						if ($is_debug)
1751
							$upcontext['actioned_items'][] = htmlspecialchars(rtrim(substr($line, 4)));
1752
					}
1753
				}
1754
1755
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
1756
				{
1757
					if ($command_line)
1758
						echo ' * ';
1759
					else
1760
						$upcontext['actioned_items'][] = $last_step;
1761
				}
1762
1763
				// Small step - only if we're actually doing stuff.
1764
				if ($do_current)
1765
					nextSubstep(++$substep);
1766
				else
1767
					$substep++;
1768
			}
1769
			elseif ($type == '{')
1770
				$current_type = 'code';
1771
			elseif ($type == '}')
1772
			{
1773
				$current_type = 'sql';
1774
1775
				if (!$do_current)
1776
				{
1777
					$current_data = '';
1778
					continue;
1779
				}
1780
1781
				if (eval('global $db_prefix, $modSettings, $smcFunc; ' . $current_data) === false)
0 ignored issues
show
The function parse_sql() contains an eval expression.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
1782
				{
1783
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
1784
					if ($command_line)
1785
						echo $upcontext['error_message'];
1786
				}
1787
1788
				// Done with code!
1789
				$current_data = '';
1790
				$done_something = true;
1791
			}
1792
1793
			continue;
1794
		}
1795
1796
		$current_data .= $line;
1797
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
1798
		{
1799
			if ((!$support_js || isset($_GET['xml'])))
1800
			{
1801
				if (!$do_current)
1802
				{
1803
					$current_data = '';
1804
					continue;
1805
				}
1806
1807
				$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));
1808
1809
				upgrade_query($current_data);
1810
1811
				// @todo This will be how it kinda does it once mysql all stripped out - needed for postgre (etc).
1812
				/*
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1813
				$result = $smcFunc['db_query']('', $current_data, false, false);
1814
				// Went wrong?
1815
				if (!$result)
1816
				{
1817
					// Bit of a bodge - do we want the error?
1818
					if (!empty($upcontext['return_error']))
1819
					{
1820
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
1821
						return false;
1822
					}
1823
				}*/
1824
				$done_something = true;
1825
			}
1826
			$current_data = '';
1827
		}
1828
		// If this is xml based and we're just getting the item name then that's grand.
1829
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
1830
		{
1831
			restore_error_handler();
1832
			return false;
1833
		}
1834
1835
		// Clean up by cleaning any step info.
1836
		$step_progress = array();
1837
		$custom_warning = '';
1838
	}
1839
1840
	// Put back the error handler.
1841
	restore_error_handler();
1842
1843
	if ($command_line)
1844
	{
1845
		echo ' Successful.' . "\n";
1846
		flush();
1847
	}
1848
1849
	$_GET['substep'] = 0;
1850
	return true;
1851
}
1852
1853
function upgrade_query($string, $unbuffered = false)
1854
{
1855
	global $db_connection, $db_server, $db_user, $db_passwd, $db_type, $command_line, $upcontext, $upgradeurl, $modSettings;
1856
	global $db_name, $db_unbuffered, $smcFunc;
1857
1858
	// Get the query result - working around some SMF specific security - just this once!
1859
	$modSettings['disableQueryCheck'] = true;
1860
	$db_unbuffered = $unbuffered;
1861
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
1862
	$db_unbuffered = false;
1863
1864
	// Failure?!
1865
	if ($result !== false)
1866
		return $result;
1867
1868
	$db_error_message = $smcFunc['db_error']($db_connection);
1869
	// If MySQL we do something more clever.
1870
	if ($db_type == 'mysql')
1871
	{
1872
		$mysqli_errno = mysqli_errno($db_connection);
1873
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR'));
1874
1875
		// Error numbers:
1876
		//    1016: Can't open file '....MYI'
1877
		//    1050: Table already exists.
1878
		//    1054: Unknown column name.
1879
		//    1060: Duplicate column name.
1880
		//    1061: Duplicate key name.
1881
		//    1062: Duplicate entry for unique key.
1882
		//    1068: Multiple primary keys.
1883
		//    1072: Key column '%s' doesn't exist in table.
1884
		//    1091: Can't drop key, doesn't exist.
1885
		//    1146: Table doesn't exist.
1886
		//    2013: Lost connection to server during query.
1887
1888
		if ($mysqli_errno == 1016)
1889
		{
1890
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
1891
			{
1892
				mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
1893
				$result = mysqli_query($db_connection, $string);
1894
				if ($result !== false)
1895
					return $result;
1896
			}
1897
		}
1898
		elseif ($mysqli_errno == 2013)
1899
		{
1900
			$db_connection = mysqli_connect($db_server, $db_user, $db_passwd);
1901
			mysqli_select_db($db_connection, $db_name);
1902
			if ($db_connection)
1903
			{
1904
				$result = mysqli_query($db_connection, $string);
1905
				if ($result !== false)
1906
					return $result;
1907
			}
1908
		}
1909
		// Duplicate column name... should be okay ;).
1910 View Code Duplication
		elseif (in_array($mysqli_errno, array(1060, 1061, 1068, 1091)))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1911
			return false;
1912
		// Duplicate insert... make sure it's the proper type of query ;).
1913 View Code Duplication
		elseif (in_array($mysqli_errno, array(1054, 1062, 1146)) && $error_query)
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1914
			return false;
1915
		// Creating an index on a non-existent column.
1916
		elseif ($mysqli_errno == 1072)
1917
			return false;
1918
		elseif ($mysqli_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
1919
			return false;
1920
	}
1921
	// If a table already exists don't go potty.
1922
	else
1923
	{
1924
		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U')))
1925
		{
1926
			if (strpos($db_error_message, 'exist') !== false)
1927
				return true;
1928
		}
1929
		elseif (strpos(trim($string), 'INSERT ') !== false)
1930
		{
1931
			if (strpos($db_error_message, 'duplicate') !== false)
1932
				return true;
1933
		}
1934
	}
1935
1936
	// Get the query string so we pass everything.
1937
	$query_string = '';
1938
	foreach ($_GET as $k => $v)
1939
		$query_string .= ';' . $k . '=' . $v;
1940
	if (strlen($query_string) != 0)
1941
		$query_string = '?' . substr($query_string, 1);
1942
1943
	if ($command_line)
1944
	{
1945
		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
1946
		die;
1947
	}
1948
1949
	// Bit of a bodge - do we want the error?
1950
	if (!empty($upcontext['return_error']))
1951
	{
1952
		$upcontext['error_message'] = $db_error_message;
1953
		$upcontext['error_string'] = $string;
1954
		return false;
1955
	}
1956
1957
	// Otherwise we have to display this somewhere appropriate if possible.
1958
	$upcontext['forced_error_message'] = '
1959
			<strong>Unsuccessful!</strong><br>
1960
1961
			<div style="margin: 2ex;">
1962
				This query:
1963
				<blockquote><pre>' . nl2br(htmlspecialchars(trim($string))) . ';</pre></blockquote>
1964
1965
				Caused the error:
1966
				<blockquote>' . nl2br(htmlspecialchars($db_error_message)) . '</blockquote>
1967
			</div>
1968
1969
			<form action="' . $upgradeurl . $query_string . '" method="post">
1970
				<input type="submit" value="Try again" class="button_submit">
1971
			</form>
1972
		</div>';
1973
1974
	upgradeExit();
1975
}
1976
1977
// This performs a table alter, but does it unbuffered so the script can time out professionally.
1978
function protected_alter($change, $substep, $is_test = false)
1979
{
1980
	global $db_prefix, $smcFunc;
1981
1982
	db_extend('packages');
1983
1984
	// Firstly, check whether the current index/column exists.
1985
	$found = false;
1986
	if ($change['type'] === 'column')
1987
	{
1988
		$columns = $smcFunc['db_list_columns']('{db_prefix}' . $change['table'], true);
1989
		foreach ($columns as $column)
1990
		{
1991
			// Found it?
1992
			if ($column['name'] === $change['name'])
1993
			{
1994
				$found |= 1;
1995
				// Do some checks on the data if we have it set.
1996
				if (isset($change['col_type']))
1997
					$found &= $change['col_type'] === $column['type'];
1998
				if (isset($change['null_allowed']))
1999
					$found &= $column['null'] == $change['null_allowed'];
2000
				if (isset($change['default']))
2001
					$found &= $change['default'] === $column['default'];
2002
			}
2003
		}
2004
	}
2005
	elseif ($change['type'] === 'index')
2006
	{
2007
		$request = upgrade_query('
2008
			SHOW INDEX
2009
			FROM ' . $db_prefix . $change['table']);
2010
		if ($request !== false)
2011
		{
2012
			$cur_index = array();
2013
2014
			while ($row = $smcFunc['db_fetch_assoc']($request))
2015
				if ($row['Key_name'] === $change['name'])
2016
					$cur_index[(int) $row['Seq_in_index']] = $row['Column_name'];
2017
2018
			ksort($cur_index, SORT_NUMERIC);
2019
			$found = array_values($cur_index) === $change['target_columns'];
2020
2021
			$smcFunc['db_free_result']($request);
2022
		}
2023
	}
2024
2025
	// If we're trying to add and it's added, we're done.
2026
	if ($found && in_array($change['method'], array('add', 'change')))
2027
		return true;
2028
	// Otherwise if we're removing and it wasn't found we're also done.
2029
	elseif (!$found && in_array($change['method'], array('remove', 'change_remove')))
2030
		return true;
2031
	// Otherwise is it just a test?
2032
	elseif ($is_test)
2033
		return false;
2034
2035
	// Not found it yet? Bummer! How about we see if we're currently doing it?
2036
	$running = false;
2037
	$found = false;
2038
	while (1 == 1)
2039
	{
2040
		$request = upgrade_query('
2041
			SHOW FULL PROCESSLIST');
2042
		while ($row = $smcFunc['db_fetch_assoc']($request))
2043
		{
2044
			if (strpos($row['Info'], 'ALTER TABLE ' . $db_prefix . $change['table']) !== false && strpos($row['Info'], $change['text']) !== false)
2045
				$found = true;
2046
		}
2047
2048
		// Can't find it? Then we need to run it fools!
2049
		if (!$found && !$running)
2050
		{
2051
			$smcFunc['db_free_result']($request);
2052
2053
			$success = upgrade_query('
2054
				ALTER TABLE ' . $db_prefix . $change['table'] . '
2055
				' . $change['text'], true) !== false;
2056
2057
			if (!$success)
2058
				return false;
2059
2060
			// Return
2061
			$running = true;
2062
		}
2063
		// What if we've not found it, but we'd ran it already? Must of completed.
2064
		elseif (!$found)
2065
		{
2066
			$smcFunc['db_free_result']($request);
2067
			return true;
2068
		}
2069
2070
		// Pause execution for a sec or three.
2071
		sleep(3);
2072
2073
		// Can never be too well protected.
2074
		nextSubstep($substep);
2075
	}
2076
2077
	// Protect it.
2078
	nextSubstep($substep);
2079
}
2080
2081
/**
2082
 * Alter a text column definition preserving its character set.
2083
 *
2084
 * @param array $change
2085
 * @param int $substep
2086
 */
2087
function textfield_alter($change, $substep)
2088
{
2089
	global $db_prefix, $smcFunc;
2090
2091
	$request = $smcFunc['db_query']('', '
2092
		SHOW FULL COLUMNS
2093
		FROM {db_prefix}' . $change['table'] . '
2094
		LIKE {string:column}',
2095
		array(
2096
			'column' => $change['column'],
2097
			'db_error_skip' => true,
2098
		)
2099
	);
2100
	if ($smcFunc['db_num_rows']($request) === 0)
2101
		die('Unable to find column ' . $change['column'] . ' inside table ' . $db_prefix . $change['table']);
2102
	$table_row = $smcFunc['db_fetch_assoc']($request);
2103
	$smcFunc['db_free_result']($request);
2104
2105
	// If something of the current column definition is different, fix it.
2106
	$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']);
2107
2108
	// Columns that previously allowed null, need to be converted first.
2109
	$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
2110
2111
	// Get the character set that goes with the collation of the column.
2112 View Code Duplication
	if ($column_fix && !empty($table_row['Collation']))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2113
	{
2114
		$request = $smcFunc['db_query']('', '
2115
			SHOW COLLATION
2116
			LIKE {string:collation}',
2117
			array(
2118
				'collation' => $table_row['Collation'],
2119
				'db_error_skip' => true,
2120
			)
2121
		);
2122
		// No results? Just forget it all together.
2123
		if ($smcFunc['db_num_rows']($request) === 0)
2124
			unset($table_row['Collation']);
2125
		else
2126
			$collation_info = $smcFunc['db_fetch_assoc']($request);
2127
		$smcFunc['db_free_result']($request);
2128
	}
2129
2130
	if ($column_fix)
2131
	{
2132
		// Make sure there are no NULL's left.
2133
		if ($null_fix)
2134
			$smcFunc['db_query']('', '
2135
				UPDATE {db_prefix}' . $change['table'] . '
2136
				SET ' . $change['column'] . ' = {string:default}
2137
				WHERE ' . $change['column'] . ' IS NULL',
2138
				array(
2139
					'default' => isset($change['default']) ? $change['default'] : '',
2140
					'db_error_skip' => true,
2141
				)
2142
			);
2143
2144
		// Do the actual alteration.
2145
		$smcFunc['db_query']('', '
2146
			ALTER TABLE {db_prefix}' . $change['table'] . '
2147
			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}' : ''),
2148
			array(
2149
				'default' => isset($change['default']) ? $change['default'] : '',
2150
				'db_error_skip' => true,
2151
			)
2152
		);
2153
	}
2154
	nextSubstep($substep);
2155
}
2156
2157
// Check if we need to alter this query.
2158
function checkChange(&$change)
2159
{
2160
	global $smcFunc, $db_type, $databases;
2161
	static $database_version, $where_field_support;
2162
2163
	// Attempt to find a database_version.
2164
	if (empty($database_version))
2165
	{
2166
		$database_version = $databases[$db_type]['version_check'];
2167
		$where_field_support = $db_type == 'mysql' && version_compare('5.0', $database_version, '<=');
2168
	}
2169
2170
	// Not a column we need to check on?
2171
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
2172
		return;
2173
2174
	// Break it up you (six|seven).
2175
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
2176
2177
	// Can we support a shortcut method?
2178
	if ($where_field_support)
2179
	{
2180
		// Get the details about this change.
2181
		$request = $smcFunc['db_query']('', '
2182
			SHOW FIELDS
2183
			FROM {db_prefix}{raw:table}
2184
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
2185
			array(
2186
				'table' => $change['table'],
2187
				'old_name' => $temp[1],
2188
				'new_name' => $temp[2],
2189
		));
2190
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2191
		if ($smcFunc['db_num_rows'] != 1)
2192
			return;
2193
2194
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
2195
		$smcFunc['db_free_result']($request);
2196
	}
2197
	else
2198
	{
2199
		// Do this the old fashion, sure method way.
2200
		$request = $smcFunc['db_query']('', '
2201
			SHOW FIELDS
2202
			FROM {db_prefix}{raw:table}',
2203
			array(
2204
				'table' => $change['table'],
2205
		));
2206
		// Mayday!
2207
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2208
		if ($smcFunc['db_num_rows'] == 0)
2209
			return;
2210
2211
		// Oh where, oh where has my little field gone. Oh where can it be...
2212
		while ($row = $smcFunc['db_query']($request))
2213
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
2214
			{
2215
				$current_type = $row['Type'];
2216
				break;
2217
			}
2218
	}
2219
2220
	// If this doesn't match, the column may of been altered for a reason.
2221
	if (trim($current_type) != trim($temp[3]))
2222
		$temp[3] = $current_type;
0 ignored issues
show
The variable $current_type does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2223
2224
	// Piece this back together.
2225
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
2226
}
2227
2228
// The next substep.
2229
function nextSubstep($substep)
2230
{
2231
	global $start_time, $timeLimitThreshold, $command_line, $custom_warning;
2232
	global $step_progress, $is_debug, $upcontext;
2233
2234
	if ($_GET['substep'] < $substep)
2235
		$_GET['substep'] = $substep;
2236
2237
	if ($command_line)
2238
	{
2239
		if (time() - $start_time > 1 && empty($is_debug))
2240
		{
2241
			echo '.';
2242
			$start_time = time();
2243
		}
2244
		return;
2245
	}
2246
2247
	@set_time_limit(300);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
2248
	if (function_exists('apache_reset_timeout'))
2249
		@apache_reset_timeout();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
2250
2251
	if (time() - $start_time <= $timeLimitThreshold)
2252
		return;
2253
2254
	// Do we have some custom step progress stuff?
2255
	if (!empty($step_progress))
2256
	{
2257
		$upcontext['substep_progress'] = 0;
2258
		$upcontext['substep_progress_name'] = $step_progress['name'];
2259
		if ($step_progress['current'] > $step_progress['total'])
2260
			$upcontext['substep_progress'] = 99.9;
2261
		else
2262
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2263
2264
		// Make it nicely rounded.
2265
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2266
	}
2267
2268
	// If this is XML we just exit right away!
2269
	if (isset($_GET['xml']))
2270
		return upgradeExit();
2271
2272
	// We're going to pause after this!
2273
	$upcontext['pause'] = true;
2274
2275
	$upcontext['query_string'] = '';
2276
	foreach ($_GET as $k => $v)
2277
	{
2278
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2279
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2280
	}
2281
2282
	// Custom warning?
2283
	if (!empty($custom_warning))
2284
		$upcontext['custom_warning'] = $custom_warning;
2285
2286
	upgradeExit();
2287
}
2288
2289
function cmdStep0()
2290
{
2291
	global $boarddir, $sourcedir, $modSettings, $start_time, $cachedir, $databases, $db_type, $smcFunc, $upcontext;
2292
	global $is_debug;
2293
	$start_time = time();
2294
2295
	ob_end_clean();
2296
	ob_implicit_flush(true);
2297
	@set_time_limit(600);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
2298
2299
	if (!isset($_SERVER['argv']))
2300
		$_SERVER['argv'] = array();
2301
	$_GET['maint'] = 1;
2302
2303
	foreach ($_SERVER['argv'] as $i => $arg)
2304
	{
2305
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2306
			$_GET['lang'] = $match[1];
2307
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2308
			continue;
2309
		elseif ($arg == '--no-maintenance')
2310
			$_GET['maint'] = 0;
2311
		elseif ($arg == '--debug')
2312
			$is_debug = true;
2313
		elseif ($arg == '--backup')
2314
			$_POST['backup'] = 1;
2315
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2316
			$_GET['conv'] = 1;
2317
		elseif ($i != 0)
2318
		{
2319
			echo 'SMF Command-line Upgrader
2320
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2321
2322
    --language=LANG         Reset the forum\'s language to LANG.
2323
    --no-maintenance        Don\'t put the forum into maintenance mode.
2324
    --debug                 Output debugging information.
2325
    --backup                Create backups of tables with "backup_" prefix.';
2326
			echo "\n";
2327
			exit;
2328
		}
2329
	}
2330
2331
	if (!php_version_check())
2332
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
2333
	if (!db_version_check())
2334
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2335
2336
	// Do some checks to make sure they have proper privileges
2337
	db_extend('packages');
2338
2339
	// CREATE
2340
	$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');
2341
2342
	// ALTER
2343
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'tinytext', 'null' => false, 'default' => ''));
2344
2345
	// DROP
2346
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2347
2348
	// Sorry... we need CREATE, ALTER and DROP
2349 View Code Duplication
	if (!$create || !$alter || !$drop)
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2350
		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);
2351
2352
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2353
		&& @file_exists($sourcedir . '/QueryString.php')
2354
		&& @file_exists($sourcedir . '/ManageBoards.php');
2355
	if (!$check && !isset($modSettings['smfVersion']))
2356
		print_error('Error: Some files are missing or out-of-date.', true);
2357
2358
	// Do a quick version spot check.
2359
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
2360
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2361
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2362
		print_error('Error: Some files have not yet been updated properly.');
2363
2364
	// Make sure Settings.php is writable.
2365
		quickFileWritable($boarddir . '/Settings.php');
2366
	if (!is_writable($boarddir . '/Settings.php'))
2367
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2368
2369
	// Make sure Settings_bak.php is writable.
2370
		quickFileWritable($boarddir . '/Settings_bak.php');
2371
	if (!is_writable($boarddir . '/Settings_bak.php'))
2372
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2373
2374 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2375
		print_error('Error: Unable to obtain write access to "agreement.txt".');
2376
	elseif (isset($modSettings['agreement']))
2377
	{
2378
		$fp = fopen($boarddir . '/agreement.txt', 'w');
2379
		fwrite($fp, $modSettings['agreement']);
2380
		fclose($fp);
2381
	}
2382
2383
	// Make sure Themes is writable.
2384
	quickFileWritable($modSettings['theme_dir']);
2385
2386
	if (!is_writable($modSettings['theme_dir']) && !isset($modSettings['smfVersion']))
2387
		print_error('Error: Unable to obtain write access to "Themes".');
2388
2389
	// Make sure cache directory exists and is writable!
2390
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
2391
	if (!file_exists($cachedir_temp))
2392
		@mkdir($cachedir_temp);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
2393
2394
	// Make sure the cache temp dir is writable.
2395
	quickFileWritable($cachedir_temp);
2396
2397
	if (!is_writable($cachedir_temp))
2398
		print_error('Error: Unable to obtain write access to "cache".', true);
2399
2400
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
2401
		print_error('Error: Unable to find language files!', true);
2402
	else
2403
	{
2404
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
2405
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
2406
2407
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
2408
			print_error('Error: Language files out of date.', true);
2409
		if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
2410
			print_error('Error: Install language is missing for selected language.', true);
2411
2412
		// Otherwise include it!
2413
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
2414
	}
2415
2416
	// Make sure we skip the HTML for login.
2417
	$_POST['upcont'] = true;
2418
	$upcontext['current_step'] = 1;
2419
}
2420
2421
/**
2422
 * Handles converting your database to UTF-8
2423
 */
2424
function ConvertUtf8()
2425
{
2426
	global $upcontext, $db_character_set, $sourcedir, $smcFunc, $modSettings, $language, $db_prefix, $db_type, $command_line, $support_js;
2427
2428
	// Done it already?
2429
	if (!empty($_POST['utf8_done']))
2430
		return true;
2431
2432
	// First make sure they aren't already on UTF-8 before we go anywhere...
2433
	if ($db_type == 'postgresql' || ($db_character_set === 'utf8' && !empty($modSettings['global_character_set']) && $modSettings['global_character_set'] === 'UTF-8'))
2434
	{
2435
		$smcFunc['db_insert']('replace',
2436
			'{db_prefix}settings',
2437
			array('variable' => 'string', 'value' => 'string'),
2438
			array(array('global_character_set', 'UTF-8')),
2439
			array('variable')
2440
		);
2441
2442
		return true;
2443
	}
2444
	else
2445
	{
2446
		$upcontext['page_title'] = 'Converting to UTF8';
2447
		$upcontext['sub_template'] = isset($_GET['xml']) ? 'convert_xml' : 'convert_utf8';
2448
2449
		// The character sets used in SMF's language files with their db equivalent.
2450
		$charsets = array(
2451
			// Armenian
2452
			'armscii8' => 'armscii8',
2453
			// Chinese-traditional.
2454
			'big5' => 'big5',
2455
			// Chinese-simplified.
2456
			'gbk' => 'gbk',
2457
			// West European.
2458
			'ISO-8859-1' => 'latin1',
2459
			// Romanian.
2460
			'ISO-8859-2' => 'latin2',
2461
			// Turkish.
2462
			'ISO-8859-9' => 'latin5',
2463
			// Latvian
2464
			'ISO-8859-13' => 'latin7',
2465
			// West European with Euro sign.
2466
			'ISO-8859-15' => 'latin9',
2467
			// Thai.
2468
			'tis-620' => 'tis620',
2469
			// Persian, Chinese, etc.
2470
			'UTF-8' => 'utf8',
2471
			// Russian.
2472
			'windows-1251' => 'cp1251',
2473
			// Greek.
2474
			'windows-1253' => 'utf8',
2475
			// Hebrew.
2476
			'windows-1255' => 'utf8',
2477
			// Arabic.
2478
			'windows-1256' => 'cp1256',
2479
		);
2480
2481
		// Get a list of character sets supported by your MySQL server.
2482
		$request = $smcFunc['db_query']('', '
2483
			SHOW CHARACTER SET',
2484
			array(
2485
			)
2486
		);
2487
		$db_charsets = array();
2488
		while ($row = $smcFunc['db_fetch_assoc']($request))
2489
			$db_charsets[] = $row['Charset'];
2490
2491
		$smcFunc['db_free_result']($request);
2492
2493
		// Character sets supported by both MySQL and SMF's language files.
2494
		$charsets = array_intersect($charsets, $db_charsets);
2495
2496
		// Use the messages.body column as indicator for the database charset.
2497
		$request = $smcFunc['db_query']('', '
2498
			SHOW FULL COLUMNS
2499
			FROM {db_prefix}messages
2500
			LIKE {string:body_like}',
2501
			array(
2502
				'body_like' => 'body',
2503
			)
2504
		);
2505
		$column_info = $smcFunc['db_fetch_assoc']($request);
2506
		$smcFunc['db_free_result']($request);
2507
2508
		// A collation looks like latin1_swedish. We only need the character set.
2509
		list($upcontext['database_charset']) = explode('_', $column_info['Collation']);
2510
		$upcontext['database_charset'] = in_array($upcontext['database_charset'], $charsets) ? array_search($upcontext['database_charset'], $charsets) : $upcontext['database_charset'];
2511
2512
		// Detect whether a fulltext index is set.
2513
		$request = $smcFunc['db_query']('', '
2514
 			SHOW INDEX
2515
	  	    FROM {db_prefix}messages',
2516
			array(
2517
			)
2518
		);
2519
2520
		$upcontext['dropping_index'] = false;
2521
2522
		// If there's a fulltext index, we need to drop it first...
2523 View Code Duplication
		if ($request !== false || $smcFunc['db_num_rows']($request) != 0)
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2524
		{
2525
			while ($row = $smcFunc['db_fetch_assoc']($request))
2526
				if ($row['Column_name'] == 'body' && (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT' || isset($row['Comment']) && $row['Comment'] == 'FULLTEXT'))
2527
					$upcontext['fulltext_index'][] = $row['Key_name'];
2528
			$smcFunc['db_free_result']($request);
2529
2530
			if (isset($upcontext['fulltext_index']))
2531
				$upcontext['fulltext_index'] = array_unique($upcontext['fulltext_index']);
2532
		}
2533
2534
		// Drop it and make a note...
2535
		if (!empty($upcontext['fulltext_index']))
2536
		{
2537
			$upcontext['dropping_index'] = true;
2538
2539
			$smcFunc['db_query']('', '
2540
  			ALTER TABLE {db_prefix}messages
2541
	  		DROP INDEX ' . implode(',
2542
		  	DROP INDEX ', $upcontext['fulltext_index']),
2543
				array(
2544
					'db_error_skip' => true,
2545
				)
2546
			);
2547
2548
			// Update the settings table
2549
			$smcFunc['db_insert']('replace',
2550
				'{db_prefix}settings',
2551
				array('variable' => 'string', 'value' => 'string'),
2552
				array('db_search_index', ''),
2553
				array('variable')
2554
			);
2555
		}
2556
2557
		// Figure out what charset we should be converting from...
2558
		$lang_charsets = array(
2559
			'arabic' => 'windows-1256',
2560
			'armenian_east' => 'armscii-8',
2561
			'armenian_west' => 'armscii-8',
2562
			'azerbaijani_latin' => 'ISO-8859-9',
2563
			'bangla' => 'UTF-8',
2564
			'belarusian' => 'ISO-8859-5',
2565
			'bulgarian' => 'windows-1251',
2566
			'cambodian' => 'UTF-8',
2567
			'chinese_simplified' => 'gbk',
2568
			'chinese_traditional' => 'big5',
2569
			'croation' => 'ISO-8859-2',
2570
			'czech' => 'ISO-8859-2',
2571
			'czech_informal' => 'ISO-8859-2',
2572
			'english_pirate' => 'UTF-8',
2573
			'esperanto' => 'ISO-8859-3',
2574
			'estonian' => 'ISO-8859-15',
2575
			'filipino_tagalog' => 'UTF-8',
2576
			'filipino_vasayan' => 'UTF-8',
2577
			'georgian' => 'UTF-8',
2578
			'greek' => 'ISO-8859-3',
2579
			'hebrew' => 'windows-1255',
2580
			'hungarian' => 'ISO-8859-2',
2581
			'irish' => 'UTF-8',
2582
			'japanese' => 'UTF-8',
2583
			'khmer' => 'UTF-8',
2584
			'korean' => 'UTF-8',
2585
			'kurdish_kurmanji' => 'ISO-8859-9',
2586
			'kurdish_sorani' => 'windows-1256',
2587
			'lao' => 'tis-620',
2588
			'latvian' => 'ISO-8859-13',
2589
			'lithuanian' => 'ISO-8859-4',
2590
			'macedonian' => 'UTF-8',
2591
			'malayalam' => 'UTF-8',
2592
			'mongolian' => 'UTF-8',
2593
			'nepali' => 'UTF-8',
2594
			'persian' => 'UTF-8',
2595
			'polish' => 'ISO-8859-2',
2596
			'romanian' => 'ISO-8859-2',
2597
			'russian' => 'windows-1252',
2598
			'sakha' => 'UTF-8',
2599
			'serbian_cyrillic' => 'ISO-8859-5',
2600
			'serbian_latin' => 'ISO-8859-2',
2601
			'sinhala' => 'UTF-8',
2602
			'slovak' => 'ISO-8859-2',
2603
			'slovenian' => 'ISO-8859-2',
2604
			'telugu' => 'UTF-8',
2605
			'thai' => 'tis-620',
2606
			'turkish' => 'ISO-8859-9',
2607
			'turkmen' => 'ISO-8859-9',
2608
			'ukranian' => 'windows-1251',
2609
			'urdu' => 'UTF-8',
2610
			'uzbek_cyrillic' => 'ISO-8859-5',
2611
			'uzbek_latin' => 'ISO-8859-5',
2612
			'vietnamese' => 'UTF-8',
2613
			'yoruba' => 'UTF-8'
2614
		);
2615
2616
		// Default to ISO-8859-1 unless we detected another supported charset
2617
		$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';
2618
2619
		$upcontext['charset_list'] = array_keys($charsets);
2620
2621
		// Translation table for the character sets not native for MySQL.
2622
		$translation_tables = array(
2623
			'windows-1255' => array(
2624
				'0x81' => '\'\'',		'0x8A' => '\'\'',		'0x8C' => '\'\'',
2625
				'0x8D' => '\'\'',		'0x8E' => '\'\'',		'0x8F' => '\'\'',
2626
				'0x90' => '\'\'',		'0x9A' => '\'\'',		'0x9C' => '\'\'',
2627
				'0x9D' => '\'\'',		'0x9E' => '\'\'',		'0x9F' => '\'\'',
2628
				'0xCA' => '\'\'',		'0xD9' => '\'\'',		'0xDA' => '\'\'',
2629
				'0xDB' => '\'\'',		'0xDC' => '\'\'',		'0xDD' => '\'\'',
2630
				'0xDE' => '\'\'',		'0xDF' => '\'\'',		'0xFB' => '0xD792',
2631
				'0xFC' => '0xE282AC',		'0xFF' => '0xD6B2',		'0xC2' => '0xFF',
2632
				'0x80' => '0xFC',		'0xE2' => '0xFB',		'0xA0' => '0xC2A0',
2633
				'0xA1' => '0xC2A1',		'0xA2' => '0xC2A2',		'0xA3' => '0xC2A3',
2634
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2635
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2636
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2637
				'0xAF' => '0xC2AF',		'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',
2638
				'0xB2' => '0xC2B2',		'0xB3' => '0xC2B3',		'0xB4' => '0xC2B4',
2639
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2640
				'0xB8' => '0xC2B8',		'0xB9' => '0xC2B9',		'0xBB' => '0xC2BB',
2641
				'0xBC' => '0xC2BC',		'0xBD' => '0xC2BD',		'0xBE' => '0xC2BE',
2642
				'0xBF' => '0xC2BF',		'0xD7' => '0xD7B3',		'0xD1' => '0xD781',
2643
				'0xD4' => '0xD7B0',		'0xD5' => '0xD7B1',		'0xD6' => '0xD7B2',
2644
				'0xE0' => '0xD790',		'0xEA' => '0xD79A',		'0xEC' => '0xD79C',
2645
				'0xED' => '0xD79D',		'0xEE' => '0xD79E',		'0xEF' => '0xD79F',
2646
				'0xF0' => '0xD7A0',		'0xF1' => '0xD7A1',		'0xF2' => '0xD7A2',
2647
				'0xF3' => '0xD7A3',		'0xF5' => '0xD7A5',		'0xF6' => '0xD7A6',
2648
				'0xF7' => '0xD7A7',		'0xF8' => '0xD7A8',		'0xF9' => '0xD7A9',
2649
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2650
				'0x86' => '0xE280A0',	'0x87' => '0xE280A1',	'0x89' => '0xE280B0',
2651
				'0x8B' => '0xE280B9',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2652
				'0x95' => '0xE280A2',	'0x97' => '0xE28094',	'0x99' => '0xE284A2',
2653
				'0xC0' => '0xD6B0',		'0xC1' => '0xD6B1',		'0xC3' => '0xD6B3',
2654
				'0xC4' => '0xD6B4',		'0xC5' => '0xD6B5',		'0xC6' => '0xD6B6',
2655
				'0xC7' => '0xD6B7',		'0xC8' => '0xD6B8',		'0xC9' => '0xD6B9',
2656
				'0xCB' => '0xD6BB',		'0xCC' => '0xD6BC',		'0xCD' => '0xD6BD',
2657
				'0xCE' => '0xD6BE',		'0xCF' => '0xD6BF',		'0xD0' => '0xD780',
2658
				'0xD2' => '0xD782',		'0xE3' => '0xD793',		'0xE4' => '0xD794',
2659
				'0xE5' => '0xD795',		'0xE7' => '0xD797',		'0xE9' => '0xD799',
2660
				'0xFD' => '0xE2808E',	'0xFE' => '0xE2808F',	'0x92' => '0xE28099',
2661
				'0x83' => '0xC692',		'0xD3' => '0xD783',		'0x88' => '0xCB86',
2662
				'0x98' => '0xCB9C',		'0x91' => '0xE28098',	'0x96' => '0xE28093',
2663
				'0xBA' => '0xC3B7',		'0x9B' => '0xE280BA',	'0xAA' => '0xC397',
2664
				'0xA4' => '0xE282AA',	'0xE1' => '0xD791',		'0xE6' => '0xD796',
2665
				'0xE8' => '0xD798',		'0xEB' => '0xD79B',		'0xF4' => '0xD7A4',
2666
				'0xFA' => '0xD7AA',
2667
			),
2668
			'windows-1253' => array(
2669
				'0x81' => '\'\'',			'0x88' => '\'\'',			'0x8A' => '\'\'',
2670
				'0x8C' => '\'\'',			'0x8D' => '\'\'',			'0x8E' => '\'\'',
2671
				'0x8F' => '\'\'',			'0x90' => '\'\'',			'0x98' => '\'\'',
2672
				'0x9A' => '\'\'',			'0x9C' => '\'\'',			'0x9D' => '\'\'',
2673
				'0x9E' => '\'\'',			'0x9F' => '\'\'',			'0xAA' => '\'\'',
2674
				'0xD2' => '0xE282AC',			'0xFF' => '0xCE92',			'0xCE' => '0xCE9E',
2675
				'0xB8' => '0xCE88',		'0xBA' => '0xCE8A',		'0xBC' => '0xCE8C',
2676
				'0xBE' => '0xCE8E',		'0xBF' => '0xCE8F',		'0xC0' => '0xCE90',
2677
				'0xC8' => '0xCE98',		'0xCA' => '0xCE9A',		'0xCC' => '0xCE9C',
2678
				'0xCD' => '0xCE9D',		'0xCF' => '0xCE9F',		'0xDA' => '0xCEAA',
2679
				'0xE8' => '0xCEB8',		'0xEA' => '0xCEBA',		'0xEC' => '0xCEBC',
2680
				'0xEE' => '0xCEBE',		'0xEF' => '0xCEBF',		'0xC2' => '0xFF',
2681
				'0xBD' => '0xC2BD',		'0xED' => '0xCEBD',		'0xB2' => '0xC2B2',
2682
				'0xA0' => '0xC2A0',		'0xA3' => '0xC2A3',		'0xA4' => '0xC2A4',
2683
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2684
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2685
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2686
				'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',		'0xB3' => '0xC2B3',
2687
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2688
				'0xBB' => '0xC2BB',		'0xE2' => '0xCEB2',		'0x80' => '0xD2',
2689
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2690
				'0x86' => '0xE280A0',	'0xA1' => '0xCE85',		'0xA2' => '0xCE86',
2691
				'0x87' => '0xE280A1',	'0x89' => '0xE280B0',	'0xB9' => '0xCE89',
2692
				'0x8B' => '0xE280B9',	'0x91' => '0xE28098',	'0x99' => '0xE284A2',
2693
				'0x92' => '0xE28099',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2694
				'0x95' => '0xE280A2',	'0x96' => '0xE28093',	'0x97' => '0xE28094',
2695
				'0x9B' => '0xE280BA',	'0xAF' => '0xE28095',	'0xB4' => '0xCE84',
2696
				'0xC1' => '0xCE91',		'0xC3' => '0xCE93',		'0xC4' => '0xCE94',
2697
				'0xC5' => '0xCE95',		'0xC6' => '0xCE96',		'0x83' => '0xC692',
2698
				'0xC7' => '0xCE97',		'0xC9' => '0xCE99',		'0xCB' => '0xCE9B',
2699
				'0xD0' => '0xCEA0',		'0xD1' => '0xCEA1',		'0xD3' => '0xCEA3',
2700
				'0xD4' => '0xCEA4',		'0xD5' => '0xCEA5',		'0xD6' => '0xCEA6',
2701
				'0xD7' => '0xCEA7',		'0xD8' => '0xCEA8',		'0xD9' => '0xCEA9',
2702
				'0xDB' => '0xCEAB',		'0xDC' => '0xCEAC',		'0xDD' => '0xCEAD',
2703
				'0xDE' => '0xCEAE',		'0xDF' => '0xCEAF',		'0xE0' => '0xCEB0',
2704
				'0xE1' => '0xCEB1',		'0xE3' => '0xCEB3',		'0xE4' => '0xCEB4',
2705
				'0xE5' => '0xCEB5',		'0xE6' => '0xCEB6',		'0xE7' => '0xCEB7',
2706
				'0xE9' => '0xCEB9',		'0xEB' => '0xCEBB',		'0xF0' => '0xCF80',
2707
				'0xF1' => '0xCF81',		'0xF2' => '0xCF82',		'0xF3' => '0xCF83',
2708
				'0xF4' => '0xCF84',		'0xF5' => '0xCF85',		'0xF6' => '0xCF86',
2709
				'0xF7' => '0xCF87',		'0xF8' => '0xCF88',		'0xF9' => '0xCF89',
2710
				'0xFA' => '0xCF8A',		'0xFB' => '0xCF8B',		'0xFC' => '0xCF8C',
2711
				'0xFD' => '0xCF8D',		'0xFE' => '0xCF8E',
2712
			),
2713
		);
2714
2715
		// Make some preparations.
2716
		if (isset($translation_tables[$upcontext['charset_detected']]))
2717
		{
2718
			$replace = '%field%';
2719
2720
			// Build a huge REPLACE statement...
2721
			foreach ($translation_tables[$upcontext['charset_detected']] as $from => $to)
2722
				$replace = 'REPLACE(' . $replace . ', ' . $from . ', ' . $to . ')';
2723
		}
2724
2725
		// Get a list of table names ahead of time... This makes it easier to set our substep and such
2726
		db_extend();
2727
		$queryTables = $smcFunc['db_list_tables'](false, $db_prefix . '%');
2728
2729
		$upcontext['table_count'] = count($queryTables);
2730
2731
		// What ones have we already done?
2732
		foreach ($queryTables as $id => $table)
2733
			if ($id < $_GET['substep'])
2734
				$upcontext['previous_tables'][] = $table;
2735
2736
		$upcontext['cur_table_num'] = $_GET['substep'];
2737
		$upcontext['cur_table_name'] = str_replace($db_prefix, '', $queryTables[$_GET['substep']]);
2738
		$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2739
2740
		// Make sure we're ready & have painted the template before proceeding
2741
		if ($support_js && !isset($_GET['xml'])) {
2742
			$_GET['substep'] = 0;
2743
			return false;
2744
		}
2745
2746
		// We want to start at the first table.
2747
		for ($substep = $_GET['substep'], $n = count($queryTables); $substep < $n; $substep++)
2748
		{
2749
			$table = $queryTables[$substep];
2750
2751
			$getTableStatus = $smcFunc['db_query']('', '
2752
				SHOW TABLE STATUS
2753
				LIKE {string:table_name}',
2754
				array(
2755
					'table_name' => str_replace('_', '\_', $table)
2756
				)
2757
			);
2758
2759
			// Only one row so we can just fetch_assoc and free the result...
2760
			$table_info = $smcFunc['db_fetch_assoc']($getTableStatus);
2761
			$smcFunc['db_free_result']($getTableStatus);
2762
2763
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($queryTables[$substep + 1]) ? $queryTables[$substep + 1] : $queryTables[$substep]));
2764
			$upcontext['cur_table_num'] = $substep + 1;
2765
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2766
2767
			// Do we need to pause?
2768
			nextSubstep($substep);
2769
2770
			// Just to make sure it doesn't time out.
2771
			if (function_exists('apache_reset_timeout'))
2772
				@apache_reset_timeout();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
2773
2774
			$table_charsets = array();
2775
2776
			// Loop through each column.
2777
			$queryColumns = $smcFunc['db_query']('', '
2778
				SHOW FULL COLUMNS
2779
				FROM ' . $table_info['Name'],
2780
				array(
2781
				)
2782
			);
2783
			while ($column_info = $smcFunc['db_fetch_assoc']($queryColumns))
2784
			{
2785
				// Only text'ish columns have a character set and need converting.
2786
				if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false)
2787
				{
2788
					$collation = empty($column_info['Collation']) || $column_info['Collation'] === 'NULL' ? $table_info['Collation'] : $column_info['Collation'];
2789
					if (!empty($collation) && $collation !== 'NULL')
2790
					{
2791
						list($charset) = explode('_', $collation);
2792
2793
						// Build structure of columns to operate on organized by charset; only operate on columns not yet utf8
2794
						if ($charset != 'utf8') {
2795
							if (!isset($table_charsets[$charset]))
2796
								$table_charsets[$charset] = array();
2797
2798
							$table_charsets[$charset][] = $column_info;
2799
						}
2800
					}
2801
				}
2802
			}
2803
			$smcFunc['db_free_result']($queryColumns);
2804
2805
			// Only change the non-utf8 columns identified above
2806
			if (count($table_charsets) > 0)
2807
			{
2808
				$updates_blob = '';
2809
				$updates_text = '';
2810
				foreach ($table_charsets as $charset => $columns)
2811
				{
2812
					if ($charset !== $charsets[$upcontext['charset_detected']])
2813
					{
2814
						foreach ($columns as $column)
2815
						{
2816
							$updates_blob .= '
2817
								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'] . '\'') . ',';
2818
							$updates_text .= '
2819
								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'] . '\'') . ',';
2820
						}
2821
					}
2822
				}
2823
2824
				// Change the columns to binary form.
2825
				$smcFunc['db_query']('', '
2826
					ALTER TABLE {raw:table_name}{raw:updates_blob}',
2827
					array(
2828
						'table_name' => $table_info['Name'],
2829
						'updates_blob' => substr($updates_blob, 0, -1),
2830
					)
2831
				);
2832
2833
				// Convert the character set if MySQL has no native support for it.
2834
				if (isset($translation_tables[$upcontext['charset_detected']]))
2835
				{
2836
					$update = '';
2837
					foreach ($table_charsets as $charset => $columns)
2838
						foreach ($columns as $column)
2839
							$update .= '
2840
								' . $column['Field'] . ' = ' . strtr($replace, array('%field%' => $column['Field'])) . ',';
0 ignored issues
show
The variable $replace does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2841
2842
					$smcFunc['db_query']('', '
2843
						UPDATE {raw:table_name}
2844
						SET {raw:updates}',
2845
						array(
2846
							'table_name' => $table_info['Name'],
2847
							'updates' => substr($update, 0, -1),
2848
						)
2849
					);
2850
				}
2851
2852
				// Change the columns back, but with the proper character set.
2853
				$smcFunc['db_query']('', '
2854
					ALTER TABLE {raw:table_name}{raw:updates_text}',
2855
					array(
2856
						'table_name' => $table_info['Name'],
2857
						'updates_text' => substr($updates_text, 0, -1),
2858
					)
2859
				);
2860
			}
2861
2862
			// Now do the actual conversion (if still needed).
2863
			if ($charsets[$upcontext['charset_detected']] !== 'utf8')
2864
			{
2865
				if ($command_line)
2866
					echo 'Converting table ' . $table_info['Name'] . ' to UTF-8...';
2867
2868
				$smcFunc['db_query']('', '
2869
					ALTER TABLE {raw:table_name}
2870
					CONVERT TO CHARACTER SET utf8',
2871
					array(
2872
						'table_name' => $table_info['Name'],
2873
					)
2874
				);
2875
2876
				if ($command_line)
2877
					echo " done.\n";
2878
			}
2879
			// If this is XML to keep it nice for the user do one table at a time anyway!
2880
			if (isset($_GET['xml']) && $upcontext['cur_table_num'] < $upcontext['table_count'])
2881
				return upgradeExit();
2882
		}
2883
2884
		$prev_charset = empty($translation_tables[$upcontext['charset_detected']]) ? $charsets[$upcontext['charset_detected']] : $translation_tables[$upcontext['charset_detected']];
2885
2886
		$smcFunc['db_insert']('replace',
2887
			'{db_prefix}settings',
2888
			array('variable' => 'string', 'value' => 'string'),
2889
			array(array('global_character_set', 'UTF-8'), array('previousCharacterSet', $prev_charset)),
2890
			array('variable')
2891
		);
2892
2893
		// Store it in Settings.php too because it's needed before db connection.
2894
		// Hopefully this works...
2895
		require_once($sourcedir . '/Subs-Admin.php');
2896
		updateSettingsFile(array('db_character_set' => '\'utf8\''));
2897
2898
		// The conversion might have messed up some serialized strings. Fix them!
2899
		$request = $smcFunc['db_query']('', '
2900
			SELECT id_action, extra
2901
			FROM {db_prefix}log_actions
2902
			WHERE action IN ({string:remove}, {string:delete})',
2903
			array(
2904
				'remove' => 'remove',
2905
				'delete' => 'delete',
2906
			)
2907
		);
2908 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2909
		{
2910
			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)
2911
				$smcFunc['db_query']('', '
2912
					UPDATE {db_prefix}log_actions
2913
					SET extra = {string:extra}
2914
					WHERE id_action = {int:current_action}',
2915
					array(
2916
						'current_action' => $row['id_action'],
2917
						'extra' => $matches[1] . strlen($matches[3]) . ':"' . $matches[3] . '"' . $matches[4],
2918
					)
2919
				);
2920
		}
2921
		$smcFunc['db_free_result']($request);
2922
2923
		if ($upcontext['dropping_index'] && $command_line)
2924
		{
2925
			echo "\nYour fulltext search index was dropped to facilitate the conversion. You will need to recreate it.";
2926
			flush();
2927
		}
2928
	}
2929
	$_GET['substep'] = 0;
2930
	return false;
2931
}
2932
2933
function serialize_to_json()
2934
{
2935
	global $command_line, $smcFunc, $modSettings, $sourcedir, $upcontext, $support_js;
2936
2937
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'serialize_json_xml' : 'serialize_json';
2938
	// First thing's first - did we already do this?
2939
	if (!empty($modSettings['json_done']))
2940
	{
2941
		if ($command_line)
2942
			return DeleteUpgrade();
2943
		else
2944
			return true;
2945
	}
2946
2947
	// Done it already - js wise?
2948
	if (!empty($_POST['json_done']))
2949
		return true;
2950
2951
	// List of tables affected by this function
2952
	// name => array('key', col1[,col2|true[,col3]])
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2953
	// If 3rd item in array is true, it indicates that col1 could be empty...
2954
	$tables = array(
2955
		'background_tasks' => array('id_task', 'task_data'),
2956
		'log_actions' => array('id_action', 'extra'),
2957
		'log_online' => array('session', 'url'),
2958
		'log_packages' => array('id_install', 'db_changes', 'failed_steps', 'credits'),
2959
		'log_spider_hits' => array('id_hit', 'url'),
2960
		'log_subscribed' => array('id_sublog', 'pending_details'),
2961
		'pm_rules' => array('id_rule', 'criteria', 'actions'),
2962
		'qanda' => array('id_question', 'answers'),
2963
		'subscriptions' => array('id_subscribe', 'cost'),
2964
		'user_alerts' => array('id_alert', 'extra', true),
2965
		'user_drafts' => array('id_draft', 'to_list', true),
2966
		// These last two are a bit different - we'll handle those separately
2967
		'settings' => array(),
2968
		'themes' => array()
2969
	);
2970
2971
	// Set up some context stuff...
2972
	// Because we're not using numeric indices, we need this to figure out the current table name...
2973
	$keys = array_keys($tables);
2974
2975
	$upcontext['page_title'] = 'Converting to JSON';
2976
	$upcontext['table_count'] = count($keys);
2977
	$upcontext['cur_table_num'] = $_GET['substep'];
2978
	$upcontext['cur_table_name'] = isset($keys[$_GET['substep']]) ? $keys[$_GET['substep']] : $keys[0];
2979
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2980
2981 View Code Duplication
	foreach ($keys as $id => $table)
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2982
		if ($id < $_GET['substep'])
2983
			$upcontext['previous_tables'][] = $table;
2984
2985
	if ($command_line)
2986
		echo 'Converting data from serialize() to json_encode().';
2987
2988
	if (!$support_js || isset($_GET['xml']))
2989
	{
2990
		// Fix the data in each table
2991
		for ($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
2992
		{
2993
			$upcontext['cur_table_name'] = isset($keys[$substep + 1]) ? $keys[$substep + 1] : $keys[$substep];
2994
			$upcontext['cur_table_num'] = $substep + 1;
2995
2996
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2997
2998
			// Do we need to pause?
2999
			nextSubstep($substep);
3000
3001
			// Initialize a few things...
3002
			$where = '';
3003
			$vars = array();
3004
			$table = $keys[$substep];
3005
			$info = $tables[$table];
3006
3007
			// Now the fun - build our queries and all that fun stuff
3008
			if ($table == 'settings')
3009
			{
3010
				// Now a few settings...
3011
				$serialized_settings = array(
3012
					'attachment_basedirectories',
3013
					'attachmentUploadDir',
3014
					'cal_today_birthday',
3015
					'cal_today_event',
3016
					'cal_today_holiday',
3017
					'displayFields',
3018
					'last_attachments_directory',
3019
					'memberlist_cache',
3020
					'search_index_custom_config',
3021
					'spider_name_cache'
3022
				);
3023
3024
				// Loop through and fix these...
3025
				$new_settings = array();
3026
				if ($command_line)
3027
					echo "\n" . 'Fixing some settings...';
3028
3029
				foreach ($serialized_settings as $var)
3030
				{
3031
					if (isset($modSettings[$var]))
3032
					{
3033
						// Attempt to unserialize the setting
3034
						$temp = @safe_unserialize($modSettings[$var]);
3035
						if (!$temp && $command_line)
3036
							echo "\n - Failed to unserialize the '" . $var . "' setting. Skipping.";
3037
						elseif ($temp !== false)
3038
							$new_settings[$var] = json_encode($temp);
3039
					}
3040
				}
3041
3042
				// Update everything at once
3043
				if (!function_exists('cache_put_data'))
3044
					require_once($sourcedir . '/Load.php');
3045
				updateSettings($new_settings, true);
3046
3047
				if ($command_line)
3048
					echo ' done.';
3049
			}
3050
			elseif ($table == 'themes')
3051
			{
3052
				// Finally, fix the admin prefs. Unfortunately this is stored per theme, but hopefully they only have one theme installed at this point...
3053
				$query = $smcFunc['db_query']('', '
3054
					SELECT id_member, id_theme, value FROM {db_prefix}themes
3055
					WHERE variable = {string:admin_prefs}',
3056
						array(
3057
							'admin_prefs' => 'admin_preferences'
3058
						)
3059
				);
3060
3061
				if ($smcFunc['db_num_rows']($query) != 0)
3062
				{
3063
					while ($row = $smcFunc['db_fetch_assoc']($query))
3064
					{
3065
						$temp = @safe_unserialize($row['value']);
3066
3067
						if ($command_line)
3068
						{
3069
							if ($temp === false)
3070
								echo "\n" . 'Unserialize of admin_preferences for user ' . $row['id_member'] . ' failed. Skipping.';
3071
							else
3072
								echo "\n" . 'Fixing admin preferences...';
3073
						}
3074
3075
						if ($temp !== false)
3076
						{
3077
							$row['value'] = json_encode($temp);
3078
3079
							// Even though we have all values from the table, UPDATE is still faster than REPLACE
3080
							$smcFunc['db_query']('', '
3081
								UPDATE {db_prefix}themes
3082
								SET value = {string:prefs}
3083
								WHERE id_theme = {int:theme}
3084
									AND id_member = {int:member}
3085
									AND variable = {string:admin_prefs}',
3086
								array(
3087
									'prefs' => $row['value'],
3088
									'theme' => $row['id_theme'],
3089
									'member' => $row['id_member'],
3090
									'admin_prefs' => 'admin_preferences'
3091
								)
3092
							);
3093
3094
							if ($command_line)
3095
								echo ' done.';
3096
						}
3097
					}
3098
3099
					$smcFunc['db_free_result']($query);
3100
				}
3101
			}
3102
			else
3103
			{
3104
				// First item is always the key...
3105
				$key = $info[0];
3106
				unset($info[0]);
3107
3108
				// Now we know what columns we have and such...
3109
				if (count($info) == 2 && $info[2] === true)
3110
				{
3111
					$col_select = $info[1];
3112
					$where = ' WHERE ' . $info[1] . ' != {empty}';
3113
				}
3114
				else
3115
				{
3116
					$col_select = implode(', ', $info);
3117
				}
3118
3119
				$query = $smcFunc['db_query']('', '
3120
					SELECT ' . $key . ', ' . $col_select . '
3121
					FROM {db_prefix}' . $table . $where,
3122
					array()
3123
				);
3124
3125
				if ($smcFunc['db_num_rows']($query) != 0)
3126
				{
3127
					if ($command_line)
3128
					{
3129
						echo "\n" . ' +++ Fixing the "' . $table . '" table...';
3130
						flush();
3131
					}
3132
3133
					while ($row = $smcFunc['db_fetch_assoc']($query))
3134
					{
3135
						$update = '';
3136
3137
						// We already know what our key is...
3138
						foreach ($info as $col)
3139
						{
3140
							if ($col !== true && $row[$col] != '')
3141
							{
3142
								$temp = @safe_unserialize($row[$col]);
3143
3144
								if ($temp === false && $command_line)
3145
								{
3146
									echo "\nFailed to unserialize " . $row[$col] . "... Skipping\n";
3147
								}
3148
								else
3149
								{
3150
									$row[$col] = json_encode($temp);
3151
3152
									// Build our SET string and variables array
3153
									$update .= (empty($update) ? '' : ', ') . $col . ' = {string:' . $col . '}';
3154
									$vars[$col] = $row[$col];
3155
								}
3156
							}
3157
						}
3158
3159
						$vars[$key] = $row[$key];
3160
3161
						// In a few cases, we might have empty data, so don't try to update in those situations...
3162
						if (!empty($update))
3163
						{
3164
							$smcFunc['db_query']('', '
3165
								UPDATE {db_prefix}' . $table . '
3166
								SET ' . $update . '
3167
								WHERE ' . $key . ' = {' . ($key == 'session' ? 'string' : 'int') . ':' . $key . '}',
3168
								$vars
3169
							);
3170
						}
3171
					}
3172
3173
					if ($command_line)
3174
						echo ' done.';
3175
3176
					// Free up some memory...
3177
					$smcFunc['db_free_result']($query);
3178
				}
3179
			}
3180
			// If this is XML to keep it nice for the user do one table at a time anyway!
3181
			if (isset($_GET['xml']))
3182
				return upgradeExit();
3183
		}
3184
3185
		if ($command_line)
3186
		{
3187
			echo "\n" . 'Successful.' . "\n";
3188
			flush();
3189
		}
3190
		$upcontext['step_progress'] = 100;
3191
3192
		// Last but not least, insert a dummy setting so we don't have to do this again in the future...
3193
		updateSettings(array('json_done' => true));
3194
3195
		$_GET['substep'] = 0;
3196
		// Make sure we move on!
3197
		if ($command_line)
3198
			return DeleteUpgrade();
3199
3200
		return true;
3201
	}
3202
3203
	// If this fails we just move on to deleting the upgrade anyway...
3204
	$_GET['substep'] = 0;
3205
	return false;
3206
}
3207
3208
/******************************************************************************
3209
******************* Templates are below this point ****************************
3210
******************************************************************************/
3211
3212
// This is what is displayed if there's any chmod to be done. If not it returns nothing...
3213
function template_chmod()
3214
{
3215
	global $upcontext, $txt, $settings;
3216
3217
	// Don't call me twice!
3218
	if (!empty($upcontext['chmod_called']))
3219
		return;
3220
3221
	$upcontext['chmod_called'] = true;
3222
3223
	// Nothing?
3224
	if (empty($upcontext['chmod']['files']) && empty($upcontext['chmod']['ftp_error']))
3225
		return;
3226
3227
	// Was it a problem with Windows?
3228
	if (!empty($upcontext['chmod']['ftp_error']) && $upcontext['chmod']['ftp_error'] == 'total_mess')
3229
	{
3230
		echo '
3231
			<div class="error_message red">
3232
				The following files need to be writable to continue the upgrade. Please ensure the Windows permissions are correctly set to allow this:<br>
3233
				<ul style="margin: 2.5ex; font-family: monospace;">
3234
					<li>' . implode('</li>
3235
					<li>', $upcontext['chmod']['files']) . '</li>
3236
				</ul>
3237
			</div>';
3238
3239
		return false;
3240
	}
3241
3242
	echo '
3243
		<div class="panel">
3244
			<h2>Your FTP connection information</h2>
3245
			<h3>The upgrader can fix any issues with file permissions to make upgrading as simple as possible. Simply enter your connection information below or alternatively click <a href="#" onclick="warning_popup();">here</a> for a list of files which need to be changed.</h3>
3246
			<script>
3247
				function warning_popup()
3248
				{
3249
					popup = window.open(\'\',\'popup\',\'height=150,width=400,scrollbars=yes\');
3250
					var content = popup.document;
3251
					content.write(\'<!DOCTYPE html>\n\');
3252
					content.write(\'<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>\n\t<head>\n\t\t<meta name="robots" content="noindex">\n\t\t\');
3253
					content.write(\'<title>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\');
3254
					content.write(\'<div class="windowbg description">\n\t\t\t<h4>The following files needs to be made writable to continue:</h4>\n\t\t\t\');
3255
					content.write(\'<p>', implode('<br>\n\t\t\t', $upcontext['chmod']['files']), '</p>\n\t\t\t\');';
3256
3257
	if (isset($upcontext['systemos']) && $upcontext['systemos'] == 'linux')
3258
		echo '
3259
					content.write(\'<hr>\n\t\t\t\');
3260
					content.write(\'<p>If you have a shell account, the convenient below command can automatically correct permissions on these files</p>\n\t\t\t\');
3261
					content.write(\'<tt># chmod a+w ', implode(' ', $upcontext['chmod']['files']), '</tt>\n\t\t\t\');';
3262
3263
	echo '
3264
					content.write(\'<a href="javascript:self.close();">close</a>\n\t\t</div>\n\t</body>\n</html>\');
3265
					content.close();
3266
				}
3267
			</script>';
3268
3269
	if (!empty($upcontext['chmod']['ftp_error']))
3270
		echo '
3271
			<div class="error_message red">
3272
				The following error was encountered when trying to connect:<br><br>
3273
				<code>', $upcontext['chmod']['ftp_error'], '</code>
3274
			</div>
3275
			<br>';
3276
3277
	if (empty($upcontext['chmod_in_form']))
3278
		echo '
3279
	<form action="', $upcontext['form_url'], '" method="post">';
3280
3281
	echo '
3282
		<table width="520" border="0" align="center" style="margin-bottom: 1ex;">
3283
			<tr>
3284
				<td width="26%" valign="top" class="textbox"><label for="ftp_server">', $txt['ftp_server'], ':</label></td>
3285
				<td>
3286
					<div style="float: right; margin-right: 1px;"><label for="ftp_port" class="textbox"><strong>', $txt['ftp_port'], ':&nbsp;</strong></label> <input type="text" size="3" name="ftp_port" id="ftp_port" value="', isset($upcontext['chmod']['port']) ? $upcontext['chmod']['port'] : '21', '" class="input_text"></div>
3287
					<input type="text" size="30" name="ftp_server" id="ftp_server" value="', isset($upcontext['chmod']['server']) ? $upcontext['chmod']['server'] : 'localhost', '" style="width: 70%;" class="input_text">
3288
					<div class="smalltext block">', $txt['ftp_server_info'], '</div>
3289
				</td>
3290
			</tr><tr>
3291
				<td width="26%" valign="top" class="textbox"><label for="ftp_username">', $txt['ftp_username'], ':</label></td>
3292
				<td>
3293
					<input type="text" size="50" name="ftp_username" id="ftp_username" value="', isset($upcontext['chmod']['username']) ? $upcontext['chmod']['username'] : '', '" style="width: 99%;" class="input_text">
3294
					<div class="smalltext block">', $txt['ftp_username_info'], '</div>
3295
				</td>
3296
			</tr><tr>
3297
				<td width="26%" valign="top" class="textbox"><label for="ftp_password">', $txt['ftp_password'], ':</label></td>
3298
				<td>
3299
					<input type="password" size="50" name="ftp_password" id="ftp_password" style="width: 99%;" class="input_password">
3300
					<div class="smalltext block">', $txt['ftp_password_info'], '</div>
3301
				</td>
3302
			</tr><tr>
3303
				<td width="26%" valign="top" class="textbox"><label for="ftp_path">', $txt['ftp_path'], ':</label></td>
3304
				<td style="padding-bottom: 1ex;">
3305
					<input type="text" size="50" name="ftp_path" id="ftp_path" value="', isset($upcontext['chmod']['path']) ? $upcontext['chmod']['path'] : '', '" style="width: 99%;" class="input_text">
3306
					<div class="smalltext block">', !empty($upcontext['chmod']['path']) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'], '</div>
3307
				</td>
3308
			</tr>
3309
		</table>
3310
3311
		<div class="righttext" style="margin: 1ex;"><input type="submit" value="', $txt['ftp_connect'], '" class="button_submit"></div>
3312
	</div>';
3313
3314
	if (empty($upcontext['chmod_in_form']))
3315
		echo '
3316
	</form>';
3317
}
3318
3319
function template_upgrade_above()
3320
{
3321
	global $modSettings, $txt, $settings, $upcontext, $upgradeurl;
3322
3323
	echo '<!DOCTYPE html>
3324
<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>
3325
	<head>
3326
		<meta charset="', isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8', '">
3327
		<meta name="robots" content="noindex">
3328
		<title>', $txt['upgrade_upgrade_utility'], '</title>
3329
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/index.css?alp21">
3330
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/install.css?alp21">
3331
		', $txt['lang_rtl'] == true ? '<link rel="stylesheet" href="' . $settings['default_theme_url'] . '/css/rtl.css?alp21">' : '', '
3332
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
3333
		<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
3334
		<script>
3335
			var smf_scripturl = \'', $upgradeurl, '\';
3336
			var smf_charset = \'', (empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'UTF-8' : $txt['lang_character_set']) : $modSettings['global_character_set']), '\';
3337
			var startPercent = ', $upcontext['overall_percent'], ';
3338
3339
			// This function dynamically updates the step progress bar - and overall one as required.
3340
			function updateStepProgress(current, max, overall_weight)
3341
			{
3342
				// What out the actual percent.
3343
				var width = parseInt((current / max) * 100);
3344
				if (document.getElementById(\'step_progress\'))
3345
				{
3346
					document.getElementById(\'step_progress\').style.width = width + "%";
3347
					setInnerHTML(document.getElementById(\'step_text\'), width + "%");
3348
				}
3349
				if (overall_weight && document.getElementById(\'overall_progress\'))
3350
				{
3351
					overall_width = parseInt(startPercent + width * (overall_weight / 100));
3352
					document.getElementById(\'overall_progress\').style.width = overall_width + "%";
3353
					setInnerHTML(document.getElementById(\'overall_text\'), overall_width + "%");
3354
				}
3355
			}
3356
		</script>
3357
	</head>
3358
	<body>
3359
	<div id="footerfix">
3360
		<div id="header">
3361
			<h1 class="forumtitle">', $txt['upgrade_upgrade_utility'], '</h1>
3362
			<img id="smflogo" src="', $settings['default_theme_url'], '/images/smflogo.png" alt="Simple Machines Forum" title="Simple Machines Forum">
3363
		</div>
3364
	<div id="wrapper">
3365
		<div id="upper_section">
3366
			<div id="inner_section">
3367
				<div id="inner_wrap">
3368
				</div>
3369
			</div>
3370
		</div>
3371
		<div id="content_section">
3372
		<div id="main_content_section">
3373
			<div id="main_steps">
3374
				<h2>', $txt['upgrade_progress'], '</h2>
3375
				<ul>';
3376
3377 View Code Duplication
	foreach ($upcontext['steps'] as $num => $step)
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3378
		echo '
3379
						<li class="', $num < $upcontext['current_step'] ? 'stepdone' : ($num == $upcontext['current_step'] ? 'stepcurrent' : 'stepwaiting'), '">', $txt['upgrade_step'], ' ', $step[0], ': ', $step[1], '</li>';
3380
3381
	echo '
3382
					</ul>
3383
			</div>
3384
3385
			<div id="progress_bar">
3386
				<div id="overall_text">', $upcontext['overall_percent'], '%</div>
3387
				<div id="overall_progress" style="width: ', $upcontext['overall_percent'], '%;">
3388
					<span>', $txt['upgrade_overall_progress'], '</span>
3389
				</div>
3390
			</div>';
3391
3392
	if (isset($upcontext['step_progress']))
3393
		echo '
3394
				<br>
3395
				<br>
3396
				<div id="progress_bar_step">
3397
					<div id="step_text">', $upcontext['step_progress'], '%</div>
3398
					<div id="step_progress" style="width: ', $upcontext['step_progress'], '%;background-color: #ffd000;">
3399
						<span>', $txt['upgrade_step_progress'], '</span>
3400
					</div>
3401
				</div>';
3402
3403
	echo '
3404
				<div id="substep_bar_div" class="smalltext" style="float: left;width: 50%;margin-top: 0.6em;display: ', isset($upcontext['substep_progress']) ? '' : 'none', ';">', isset($upcontext['substep_progress_name']) ? trim(strtr($upcontext['substep_progress_name'], array('.' => ''))) : '', ':</div>
3405
				<div id="substep_bar_div2" style="float: left;font-size: 8pt; height: 12pt; border: 1px solid black; background-color: white; width: 33%; margin: 0.6em auto 0 6em; display: ', isset($upcontext['substep_progress']) ? '' : 'none', ';">
3406
					<div id="substep_text" style="color: #000; position: absolute; margin-left: -5em;">', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : '', '%</div>
3407
					<div id="substep_progress" style="width: ', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%; height: 12pt; z-index: 1; background-color: #eebaf4;">&nbsp;</div>
3408
				</div>';
3409
3410
	// How long have we been running this?
3411
	$elapsed = time() - $upcontext['started'];
3412
	$mins = (int) ($elapsed / 60);
3413
	$seconds = $elapsed - $mins * 60;
3414
	echo '
3415
								<br> <br> <br> <br> <br>
3416
								<div class="smalltext" style="padding: 5px; text-align: center;"><br>', $txt['upgrade_time_elapsed'], ':
3417
									<span id="mins_elapsed">', $mins, '</span> ', $txt['upgrade_time_mins'], ', <span id="secs_elapsed">', $seconds, '</span> ', $txt['upgrade_time_secs'], '.
3418
								</div>';
3419
	echo '
3420
			</div>
3421
			</div>
3422
			<div id="main_screen" class="clear">
3423
				<h2>', $upcontext['page_title'], '</h2>
3424
				<div class="panel">
3425
					<div style="max-height: 360px; overflow: auto;">';
3426
}
3427
3428
function template_upgrade_below()
3429
{
3430
	global $upcontext, $txt;
3431
3432
	if (!empty($upcontext['pause']))
3433
		echo '
3434
								<em>', $txt['upgrade_incomplete'], '.</em><br>
3435
3436
								<h2 style="margin-top: 2ex;">', $txt['upgrade_not_quite_done'], '</h2>
3437
								<h3>
3438
									', $txt['upgrade_paused_overload'], '
3439
								</h3>';
3440
3441
	if (!empty($upcontext['custom_warning']))
3442
		echo '
3443
								<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3444
									<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3445
									<strong style="text-decoration: underline;">', $txt['upgrade_note'], '</strong><br>
3446
									<div style="padding-left: 6ex;">', $upcontext['custom_warning'], '</div>
3447
								</div>';
3448
3449
	echo '
3450
								<div class="righttext" style="margin: 1ex;">';
3451
3452
	if (!empty($upcontext['continue']))
3453
		echo '
3454
									<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '"', $upcontext['continue'] == 2 ? ' disabled' : '', ' class="button_submit">';
3455
	if (!empty($upcontext['skip']))
3456
		echo '
3457
									<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="dontSubmit = true; document.getElementById(\'contbutt\').disabled = \'disabled\'; return true;" class="button_submit">';
3458
3459
	echo '
3460
								</div>
3461
							</form>
3462
						</div>
3463
				</div>
3464
			</div>
3465
			</div>
3466
		</div>
3467
		<div id="footer">
3468
			<ul>
3469
				<li class="copyright"><a href="https://www.simplemachines.org/" title="Simple Machines Forum" target="_blank" class="new_win">SMF &copy; 2017, Simple Machines</a></li>
3470
			</ul>
3471
		</div>
3472
	</body>
3473
</html>';
3474
3475
	// Are we on a pause?
3476
	if (!empty($upcontext['pause']))
3477
	{
3478
		echo '
3479
		<script>
3480
			window.onload = doAutoSubmit;
3481
			var countdown = 3;
3482
			var dontSubmit = false;
3483
3484
			function doAutoSubmit()
3485
			{
3486
				if (countdown == 0 && !dontSubmit)
3487
					document.upform.submit();
3488
				else if (countdown == -1)
3489
					return;
3490
3491
				document.getElementById(\'contbutt\').value = "', $txt['upgrade_continue'], ' (" + countdown + ")";
3492
				countdown--;
3493
3494
				setTimeout("doAutoSubmit();", 1000);
3495
			}
3496
		</script>';
3497
	}
3498
}
3499
3500
function template_xml_above()
3501
{
3502
	global $upcontext;
3503
3504
	echo '<', '?xml version="1.0" encoding="UTF-8"?', '>
3505
	<smf>';
3506
3507
	if (!empty($upcontext['get_data']))
3508
		foreach ($upcontext['get_data'] as $k => $v)
3509
			echo '
3510
		<get key="', $k, '">', $v, '</get>';
3511
}
3512
3513
function template_xml_below()
3514
{
3515
	echo '
3516
		</smf>';
3517
}
3518
3519
function template_error_message()
3520
{
3521
	global $upcontext;
3522
3523
	echo '
3524
	<div class="error_message red">
3525
		', $upcontext['error_msg'], '
3526
		<br>
3527
		<a href="', $_SERVER['PHP_SELF'], '">Click here to try again.</a>
3528
	</div>';
3529
}
3530
3531
function template_welcome_message()
0 ignored issues
show
The function template_welcome_message() has been defined more than once; this definition is ignored, only the first definition in other/install.php (L1945-1994) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
3532
{
3533
	global $upcontext, $disable_security, $settings, $txt;
3534
3535
	echo '
3536
		<script src="https://www.simplemachines.org/smf/current-version.js?version=' . SMF_VERSION . '"></script>
3537
			<h3>', sprintf($txt['upgrade_ready_proceed'], SMF_VERSION), '</h3>
3538
	<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">
3539
		<input type="hidden" name="', $upcontext['login_token_var'], '" value="', $upcontext['login_token'], '">
3540
		<div id="version_warning" style="margin: 2ex; padding: 2ex; border: 2px dashed #a92174; color: black; background-color: #fbbbe2; display: none;">
3541
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3542
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3543
			<div style="padding-left: 6ex;">
3544
				', sprintf($txt['upgrade_warning_out_of_date'], SMF_VERSION), '
3545
			</div>
3546
		</div>';
3547
3548
	$upcontext['chmod_in_form'] = true;
3549
	template_chmod();
3550
3551
	// For large, pre 1.1 RC2 forums give them a warning about the possible impact of this upgrade!
3552
	if ($upcontext['is_large_forum'])
3553
		echo '
3554
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3555
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3556
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3557
			<div style="padding-left: 6ex;">
3558
				', $txt['upgrade_warning_lots_data'], '
3559
			</div>
3560
		</div>';
3561
3562
	// A warning message?
3563
	if (!empty($upcontext['warning']))
3564
		echo '
3565
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3566
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3567
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3568
			<div style="padding-left: 6ex;">
3569
				', $upcontext['warning'], '
3570
			</div>
3571
		</div>';
3572
3573
	// Paths are incorrect?
3574
	echo '
3575
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #804840; color: black; background-color: #fe5a44; ', (file_exists($settings['default_theme_dir'] . '/scripts/script.js') ? 'display: none;' : ''), '" id="js_script_missing_error">
3576
			<div style="float: left; width: 2ex; font-size: 2em; color: black;">!!</div>
3577
			<strong style="text-decoration: underline;">', $txt['upgrade_critical_error'], '</strong><br>
3578
			<div style="padding-left: 6ex;">
3579
				', $txt['upgrade_error_script_js'], '
3580
			</div>
3581
		</div>';
3582
3583
	// Is there someone already doing this?
3584
	if (!empty($upcontext['user']['id']) && (time() - $upcontext['started'] < 72600 || time() - $upcontext['updated'] < 3600))
3585
	{
3586
		$ago = time() - $upcontext['started'];
3587
		if ($ago < 60)
3588
			$ago = $ago . ' seconds';
3589
		elseif ($ago < 3600)
3590
			$ago = (int) ($ago / 60) . ' minutes';
3591
		else
3592
			$ago = (int) ($ago / 3600) . ' hours';
3593
3594
		$active = time() - $upcontext['updated'];
3595
		if ($active < 60)
3596
			$updated = $active . ' seconds';
3597
		elseif ($active < 3600)
3598
			$updated = (int) ($active / 60) . ' minutes';
3599
		else
3600
			$updated = (int) ($active / 3600) . ' hours';
3601
3602
		echo '
3603
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3604
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3605
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3606
			<div style="padding-left: 6ex;">
3607
				&quot;', $upcontext['user']['name'], '&quot; has been running the upgrade script for the last ', $ago, ' - and was last active ', $updated, ' ago.';
3608
3609
		if ($active < 600)
3610
			echo '
3611
				We recommend that you do not run this script unless you are sure that ', $upcontext['user']['name'], ' has completed their upgrade.';
3612
3613
		if ($active > $upcontext['inactive_timeout'])
3614
			echo '
3615
				<br><br>You can choose to either run the upgrade again from the beginning - or alternatively continue from the last step reached during the last upgrade.';
3616
		else
3617
			echo '
3618
				<br><br>This upgrade script cannot be run until ', $upcontext['user']['name'], ' has been inactive for at least ', ($upcontext['inactive_timeout'] > 120 ? round($upcontext['inactive_timeout'] / 60, 1) . ' minutes!' : $upcontext['inactive_timeout'] . ' seconds!');
3619
3620
		echo '
3621
			</div>
3622
		</div>';
3623
	}
3624
3625
	echo '
3626
			<strong>Admin Login: ', $disable_security ? '(DISABLED)' : '', '</strong>
3627
			<h3>For security purposes please login with your admin account to proceed with the upgrade.</h3>
3628
			<table>
3629
				<tr valign="top">
3630
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Username:</strong></td>
3631
					<td>
3632
						<input type="text" name="user" value="', !empty($upcontext['username']) ? $upcontext['username'] : '', '"', $disable_security ? ' disabled' : '', ' class="input_text">';
3633
3634
	if (!empty($upcontext['username_incorrect']))
3635
		echo '
3636
						<div class="smalltext" style="color: red;">Username Incorrect</div>';
3637
3638
	echo '
3639
					</td>
3640
				</tr>
3641
				<tr valign="top">
3642
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Password:</strong></td>
3643
					<td>
3644
						<input type="password" name="passwrd" value=""', $disable_security ? ' disabled' : '', ' class="input_password">
3645
						<input type="hidden" name="hash_passwrd" value="">';
3646
3647
	if (!empty($upcontext['password_failed']))
3648
		echo '
3649
						<div class="smalltext" style="color: red;">Password Incorrect</div>';
3650
3651
	echo '
3652
					</td>
3653
				</tr>';
3654
3655
	// Can they continue?
3656
	if (!empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] >= $upcontext['inactive_timeout'] && $upcontext['user']['step'] > 1)
3657
	{
3658
		echo '
3659
				<tr>
3660
					<td colspan="2">
3661
						<label for="cont"><input type="checkbox" id="cont" name="cont" checked class="input_check">Continue from step reached during last execution of upgrade script.</label>
3662
					</td>
3663
				</tr>';
3664
	}
3665
3666
	echo '
3667
			</table><br>
3668
			<span class="smalltext">
3669
				<strong>Note:</strong> If necessary the above security check can be bypassed for users who may administrate a server but not have admin rights on the forum. In order to bypass the above check simply open &quot;upgrade.php&quot; in a text editor and replace &quot;$disable_security = false;&quot; with &quot;$disable_security = true;&quot; and refresh this page.
3670
			</span>
3671
			<input type="hidden" name="login_attempt" id="login_attempt" value="1">
3672
			<input type="hidden" name="js_works" id="js_works" value="0">';
3673
3674
	// Say we want the continue button!
3675
	$upcontext['continue'] = !empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] < $upcontext['inactive_timeout'] ? 2 : 1;
3676
3677
	// This defines whether javascript is going to work elsewhere :D
3678
	echo '
3679
		<script>
3680
			if (\'XMLHttpRequest\' in window && document.getElementById(\'js_works\'))
3681
				document.getElementById(\'js_works\').value = 1;
3682
3683
			// Latest version?
3684
			function smfCurrentVersion()
3685
			{
3686
				var smfVer, yourVer;
3687
3688
				if (!(\'smfVersion\' in window))
3689
					return;
3690
3691
				window.smfVersion = window.smfVersion.replace(/SMF\s?/g, \'\');
3692
3693
				smfVer = document.getElementById(\'smfVersion\');
3694
				yourVer = document.getElementById(\'yourVersion\');
3695
3696
				setInnerHTML(smfVer, window.smfVersion);
3697
3698
				var currentVersion = getInnerHTML(yourVer);
3699
				if (currentVersion < window.smfVersion)
3700
					document.getElementById(\'version_warning\').style.display = \'\';
3701
			}
3702
			addLoadEvent(smfCurrentVersion);
3703
3704
			// This checks that the script file even exists!
3705
			if (typeof(smfSelectText) == \'undefined\')
3706
				document.getElementById(\'js_script_missing_error\').style.display = \'\';
3707
3708
		</script>';
3709
}
3710
3711
function template_upgrade_options()
3712
{
3713
	global $upcontext, $modSettings, $db_prefix, $mmessage, $mtitle;
3714
3715
	echo '
3716
			<h3>Before the upgrade gets underway please review the options below - and hit continue when you\'re ready to begin.</h3>
3717
			<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">';
3718
3719
	// Warning message?
3720
	if (!empty($upcontext['upgrade_options_warning']))
3721
		echo '
3722
		<div style="margin: 1ex; padding: 1ex; border: 1px dashed #cc3344; color: black; background-color: #ffe4e9;">
3723
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3724
			<strong style="text-decoration: underline;">Warning!</strong><br>
3725
			<div style="padding-left: 4ex;">
3726
				', $upcontext['upgrade_options_warning'], '
3727
			</div>
3728
		</div>';
3729
3730
	echo '
3731
				<table>
3732
					<tr valign="top">
3733
						<td width="2%">
3734
							<input type="checkbox" name="backup" id="backup" value="1" class="input_check">
3735
						</td>
3736
						<td width="100%">
3737
							<label for="backup">Backup tables in your database with the prefix &quot;backup_' . $db_prefix . '&quot;.</label> (recommended!)
3738
						</td>
3739
					</tr>
3740
					<tr valign="top">
3741
						<td width="2%">
3742
							<input type="checkbox" name="maint" id="maint" value="1" checked class="input_check">
3743
						</td>
3744
						<td width="100%">
3745
							<label for="maint">Put the forum into maintenance mode during upgrade.</label> <span class="smalltext">(<a href="#" onclick="document.getElementById(\'mainmess\').style.display = document.getElementById(\'mainmess\').style.display == \'\' ? \'none\' : \'\'">Customize</a>)</span>
3746
							<div id="mainmess" style="display: none;">
3747
								<strong class="smalltext">Maintenance Title: </strong><br>
3748
								<input type="text" name="maintitle" size="30" value="', htmlspecialchars($mtitle), '" class="input_text"><br>
3749
								<strong class="smalltext">Maintenance Message: </strong><br>
3750
								<textarea name="mainmessage" rows="3" cols="50">', htmlspecialchars($mmessage), '</textarea>
3751
							</div>
3752
						</td>
3753
					</tr>
3754
					<tr valign="top">
3755
						<td width="2%">
3756
							<input type="checkbox" name="debug" id="debug" value="1" class="input_check">
3757
						</td>
3758
						<td width="100%">
3759
							<label for="debug">Output extra debugging information</label>
3760
						</td>
3761
					</tr>
3762
					<tr valign="top">
3763
						<td width="2%">
3764
							<input type="checkbox" name="empty_error" id="empty_error" value="1" class="input_check">
3765
						</td>
3766
						<td width="100%">
3767
							<label for="empty_error">Empty error log before upgrading</label>
3768
						</td>
3769
					</tr>';
3770
3771
	if (!empty($upcontext['karma_installed']['good']) || !empty($upcontext['karma_installed']['bad']))
3772
		echo '
3773
					<tr valign="top">
3774
						<td width="2%">
3775
							<input type="checkbox" name="delete_karma" id="delete_karma" value="1" class="input_check">
3776
						</td>
3777
						<td width="100%">
3778
							<label for="delete_karma">Delete all karma settings and info from the DB</label>
3779
						</td>
3780
					</tr>';
3781
3782
	echo '
3783
					<tr valign="top">
3784
						<td width="2%">
3785
							<input type="checkbox" name="stats" id="stats" value="1"', empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']) ? '' : ' checked="checked"', ' class="input_check" />
3786
						</td>
3787
						<td width="100%">
3788
							<label for="stat">
3789
								Allow Simple Machines to Collect Basic Stats Monthly.<br>
3790
								<span class="smalltext">If enabled, this will allow Simple Machines to visit your site once a month to collect basic statistics. This will help us make decisions as to which configurations to optimise the software for. For more information please visit our <a href="https://www.simplemachines.org/about/stats.php" target="_blank">info page</a>.</span>
3791
							</label>
3792
						</td>
3793
					</tr>
3794
				</table>
3795
				<input type="hidden" name="upcont" value="1">';
3796
3797
	// We need a normal continue button here!
3798
	$upcontext['continue'] = 1;
3799
}
3800
3801
// Template for the database backup tool/
3802
function template_backup_database()
3803
{
3804
	global $upcontext, $support_js, $is_debug;
3805
3806
	echo '
3807
			<h3>Please wait while a backup is created. For large forums this may take some time!</h3>';
3808
3809
	echo '
3810
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
3811
			<input type="hidden" name="backup_done" id="backup_done" value="0">
3812
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
3813
			<div id="debug_section" style="height: ', ($is_debug ? '115' : '12') , 'px; overflow: auto;">
3814
			<span id="debuginfo"></span>
3815
			</div>';
3816
3817
	// Dont any tables so far?
3818
	if (!empty($upcontext['previous_tables']))
3819
		foreach ($upcontext['previous_tables'] as $table)
3820
			echo '
3821
			<br>Completed Table: &quot;', $table, '&quot;.';
3822
3823
	echo '
3824
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
3825
			<br><span id="commess" style="font-weight: bold; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">Backup Complete! Click Continue to Proceed.</span>';
3826
3827
	// Continue please!
3828
	$upcontext['continue'] = $support_js ? 2 : 1;
3829
3830
	// If javascript allows we want to do this using XML.
3831
	if ($support_js)
3832
	{
3833
		echo '
3834
		<script>
3835
			var lastTable = ', $upcontext['cur_table_num'], ';
3836
			function getNextTables()
3837
			{
3838
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
3839
			}
3840
3841
			// Got an update!
3842
			function onBackupUpdate(oXMLDoc)
3843
			{
3844
				var sCurrentTableName = "";
3845
				var iTableNum = 0;
3846
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
3847
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
3848
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
3849
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
3850
3851
				// Update the page.
3852
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
3853
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
3854
				lastTable = iTableNum;
3855
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
3856
3857
		// If debug flood the screen.
3858
		if ($is_debug)
3859
			echo '
3860
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
3861
3862
				if (document.getElementById(\'debug_section\').scrollHeight)
3863
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
3864
3865
		echo '
3866
				// Get the next update...
3867
				if (iTableNum == ', $upcontext['table_count'], ')
3868
				{
3869
					document.getElementById(\'commess\').style.display = "";
3870
					document.getElementById(\'current_tab_div\').style.display = "none";
3871
					document.getElementById(\'contbutt\').disabled = 0;
3872
					document.getElementById(\'backup_done\').value = 1;
3873
				}
3874
				else
3875
					getNextTables();
3876
			}
3877
			getNextTables();
3878
		//# sourceURL=dynamicScript-bkup.js
3879
		</script>';
3880
	}
3881
}
3882
3883
function template_backup_xml()
3884
{
3885
	global $upcontext;
3886
3887
	echo '
3888
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
3889
}
3890
3891
// Here is the actual "make the changes" template!
3892
function template_database_changes()
3893
{
3894
	global $upcontext, $support_js, $is_debug, $timeLimitThreshold;
3895
3896
	if (empty($is_debug) && !empty($upcontext['upgrade_status']['debug']))
3897
		$is_debug = true;
3898
3899
	echo '
3900
		<h3>Executing database changes</h3>
3901
		<h4 style="font-style: italic;">Please be patient - this may take some time on large forums. The time elapsed increments from the server to show progress is being made!</h4>';
3902
3903
	echo '
3904
		<form action="', $upcontext['form_url'], '&amp;filecount=', $upcontext['file_count'], '" name="upform" id="upform" method="post">
3905
		<input type="hidden" name="database_done" id="database_done" value="0">';
3906
3907
	// No javascript looks rubbish!
3908
	if (!$support_js)
3909
	{
3910
		foreach ($upcontext['actioned_items'] as $num => $item)
3911
		{
3912
			if ($num != 0)
3913
				echo ' Successful!';
3914
			echo '<br>' . $item;
3915
		}
3916 View Code Duplication
		if (!empty($upcontext['changes_complete']))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3917
		{
3918
			if ($is_debug)
3919
			{
3920
				$active = time() - $upcontext['started'];
3921
				$hours = floor($active / 3600);
3922
				$minutes = intval(($active / 60) % 60);
3923
				$seconds = intval($active % 60);
3924
3925
				$totalTime = '';
3926
				if ($hours > 0)
3927
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
3928
				if ($minutes > 0)
3929
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
3930
				if ($seconds > 0)
3931
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
3932
			}
3933
3934
			if ($is_debug && !empty($totalTime))
3935
				echo ' Successful! Completed in ', $totalTime, '<br><br>';
3936
			else
3937
				echo ' Successful!<br><br>';
3938
3939
			echo '<span id="commess" style="font-weight: bold;">1 Database Updates Complete! Click Continue to Proceed.</span><br>';
3940
		}
3941
	}
3942
	else
3943
	{
3944
		// Tell them how many files we have in total.
3945
		if ($upcontext['file_count'] > 1)
3946
			echo '
3947
		<strong id="info1">Executing upgrade script <span id="file_done">', $upcontext['cur_file_num'], '</span> of ', $upcontext['file_count'], '.</strong>';
3948
3949
		echo '
3950
		<h3 id="info2"><strong>Executing:</strong> &quot;<span id="cur_item_name">', $upcontext['current_item_name'], '</span>&quot; (<span id="item_num">', $upcontext['current_item_num'], '</span> of <span id="total_items"><span id="item_count">', $upcontext['total_items'], '</span>', $upcontext['file_count'] > 1 ? ' - of this script' : '', ')</span></h3>
3951
		<br><span id="commess" style="font-weight: bold; display: ', !empty($upcontext['changes_complete']) || $upcontext['current_debug_item_num'] == $upcontext['debug_items'] ? 'inline' : 'none', ';">Database Updates Complete! Click Continue to Proceed.</span>';
3952
3953 View Code Duplication
		if ($is_debug)
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3954
		{
3955
			if ($upcontext['current_debug_item_num'] == $upcontext['debug_items'])
3956
			{
3957
				$active = time() - $upcontext['started'];
3958
				$hours = floor($active / 3600);
3959
				$minutes = intval(($active / 60) % 60);
3960
				$seconds = intval($active % 60);
3961
3962
				$totalTime = '';
3963
				if ($hours > 0)
3964
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
3965
				if ($minutes > 0)
3966
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
3967
				if ($seconds > 0)
3968
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
3969
			}
3970
3971
			echo '
3972
			<br><span id="upgradeCompleted">';
3973
3974
			if (!empty($totalTime))
3975
				echo 'Completed in ', $totalTime, '<br>';
3976
3977
			echo '</span>
3978
			<div id="debug_section" style="height: 59px; overflow: auto;">
3979
			<span id="debuginfo"></span>
3980
			</div>';
3981
		}
3982
	}
3983
3984
	// Place for the XML error message.
3985
	echo '
3986
		<div id="error_block" style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9; display: ', empty($upcontext['error_message']) ? 'none' : '', ';">
3987
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3988
			<strong style="text-decoration: underline;">Error!</strong><br>
3989
			<div style="padding-left: 6ex;" id="error_message">', isset($upcontext['error_message']) ? $upcontext['error_message'] : 'Unknown Error!', '</div>
3990
		</div>';
3991
3992
	// We want to continue at some point!
3993
	$upcontext['continue'] = $support_js ? 2 : 1;
3994
3995
	// If javascript allows we want to do this using XML.
3996
	if ($support_js)
3997
	{
3998
		echo '
3999
		<script>
4000
			var lastItem = ', $upcontext['current_debug_item_num'], ';
4001
			var sLastString = "', strtr($upcontext['current_debug_item_name'], array('"' => '&quot;')), '";
4002
			var iLastSubStepProgress = -1;
4003
			var curFile = ', $upcontext['cur_file_num'], ';
4004
			var totalItems = 0;
4005
			var prevFile = 0;
4006
			var retryCount = 0;
4007
			var testvar = 0;
4008
			var timeOutID = 0;
4009
			var getData = "";
4010
			var debugItems = ', $upcontext['debug_items'], ';';
4011
4012
		if ($is_debug)
4013
			echo '
4014
			var upgradeStartTime = ' . $upcontext['started'] . ';';
4015
4016
		echo '
4017
			function getNextItem()
4018
			{
4019
				// We want to track this...
4020
				if (timeOutID)
4021
					clearTimeout(timeOutID);
4022
				timeOutID = window.setTimeout("retTimeout()", ', $timeLimitThreshold, '000);
4023
4024
				getXMLDocument(\'', $upcontext['form_url'], '&xml&filecount=', $upcontext['file_count'], '&substep=\' + lastItem + getData, onItemUpdate);
4025
			}
4026
4027
			// Got an update!
4028
			function onItemUpdate(oXMLDoc)
4029
			{
4030
				var sItemName = "";
4031
				var sDebugName = "";
4032
				var iItemNum = 0;
4033
				var iSubStepProgress = -1;
4034
				var iDebugNum = 0;
4035
				var bIsComplete = 0;
4036
				getData = "";
4037
4038
				// We\'ve got something - so reset the timeout!
4039
				if (timeOutID)
4040
					clearTimeout(timeOutID);
4041
4042
				// Assume no error at this time...
4043
				document.getElementById("error_block").style.display = "none";
4044
4045
				// Are we getting some duff info?
4046
				if (!oXMLDoc.getElementsByTagName("item")[0])
4047
				{
4048
					// Too many errors?
4049
					if (retryCount > 15)
4050
					{
4051
						document.getElementById("error_block").style.display = "";
4052
						setInnerHTML(document.getElementById("error_message"), "Error retrieving information on step: " + (sDebugName == "" ? sLastString : sDebugName));';
4053
4054
	if ($is_debug)
4055
		echo '
4056
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4057
4058
	echo '
4059
					}
4060
					else
4061
					{
4062
						retryCount++;
4063
						getNextItem();
4064
					}
4065
					return false;
4066
				}
4067
4068
				// Never allow loops.
4069
				if (curFile == prevFile)
4070
				{
4071
					retryCount++;
4072
					if (retryCount > 10)
4073
					{
4074
						document.getElementById("error_block").style.display = "";
4075
						setInnerHTML(document.getElementById("error_message"), "Upgrade script appears to be going into a loop - step: " + sDebugName);';
4076
4077
	if ($is_debug)
4078
		echo '
4079
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4080
4081
	echo '
4082
					}
4083
				}
4084
				retryCount = 0;
4085
4086
				for (var i = 0; i < oXMLDoc.getElementsByTagName("item")[0].childNodes.length; i++)
4087
					sItemName += oXMLDoc.getElementsByTagName("item")[0].childNodes[i].nodeValue;
4088
				for (var i = 0; i < oXMLDoc.getElementsByTagName("debug")[0].childNodes.length; i++)
4089
					sDebugName += oXMLDoc.getElementsByTagName("debug")[0].childNodes[i].nodeValue;
4090
				for (var i = 0; i < oXMLDoc.getElementsByTagName("get").length; i++)
4091
				{
4092
					getData += "&" + oXMLDoc.getElementsByTagName("get")[i].getAttribute("key") + "=";
4093
					for (var j = 0; j < oXMLDoc.getElementsByTagName("get")[i].childNodes.length; j++)
4094
					{
4095
						getData += oXMLDoc.getElementsByTagName("get")[i].childNodes[j].nodeValue;
4096
					}
4097
				}
4098
4099
				iItemNum = oXMLDoc.getElementsByTagName("item")[0].getAttribute("num");
4100
				iDebugNum = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("num"));
4101
				bIsComplete = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("complete"));
4102
				iSubStepProgress = parseFloat(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("percent"));
4103
				sLastString = sDebugName + " (Item: " + iDebugNum + ")";
4104
4105
				curFile = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("num"));
4106
				debugItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("debug_items"));
4107
				totalItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("items"));
4108
4109
				// If we have an error we haven\'t completed!
4110
				if (oXMLDoc.getElementsByTagName("error")[0] && bIsComplete)
4111
					iDebugNum = lastItem;
4112
4113
				// Do we have the additional progress bar?
4114
				if (iSubStepProgress != -1)
4115
				{
4116
					document.getElementById("substep_bar_div").style.display = "";
4117
					document.getElementById("substep_bar_div2").style.display = "";
4118
					document.getElementById("substep_progress").style.width = iSubStepProgress + "%";
4119
					setInnerHTML(document.getElementById("substep_text"), iSubStepProgress + "%");
4120
					setInnerHTML(document.getElementById("substep_bar_div"), sDebugName.replace(/\./g, "") + ":");
4121
				}
4122
				else
4123
				{
4124
					document.getElementById("substep_bar_div").style.display = "none";
4125
					document.getElementById("substep_bar_div2").style.display = "none";
4126
				}
4127
4128
				// Move onto the next item?
4129
				if (bIsComplete)
4130
					lastItem = iDebugNum;
4131
				else
4132
					lastItem = iDebugNum - 1;
4133
4134
				// Are we finished?
4135
				if (bIsComplete && iDebugNum == -1 && curFile >= ', $upcontext['file_count'], ')
4136
				{';
4137
4138
		if ($is_debug)
4139
			echo '
4140
					document.getElementById(\'debug_section\').style.display = "none";
4141
4142
					var upgradeFinishedTime = parseInt(oXMLDoc.getElementsByTagName("curtime")[0].childNodes[0].nodeValue);
4143
					var diffTime = upgradeFinishedTime - upgradeStartTime;
4144
					var diffHours = Math.floor(diffTime / 3600);
4145
					var diffMinutes = parseInt((diffTime / 60) % 60);
4146
					var diffSeconds = parseInt(diffTime % 60);
4147
4148
					var totalTime = "";
4149
					if (diffHours > 0)
4150
						totalTime = totalTime + diffHours + " hour" + (diffHours > 1 ? "s" : "") + " ";
4151
					if (diffMinutes > 0)
4152
						totalTime = totalTime + diffMinutes + " minute" + (diffMinutes > 1 ? "s" : "") + " ";
4153
					if (diffSeconds > 0)
4154
						totalTime = totalTime + diffSeconds + " second" + (diffSeconds > 1 ? "s" : "");
4155
4156
					setInnerHTML(document.getElementById("upgradeCompleted"), "Completed in " + totalTime);';
4157
4158
		echo '
4159
4160
					document.getElementById(\'commess\').style.display = "";
4161
					document.getElementById(\'contbutt\').disabled = 0;
4162
					document.getElementById(\'database_done\').value = 1;';
4163
4164
		if ($upcontext['file_count'] > 1)
4165
			echo '
4166
					document.getElementById(\'info1\').style.display = "none";';
4167
4168
		echo '
4169
					document.getElementById(\'info2\').style.display = "none";
4170
					updateStepProgress(100, 100, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4171
					return true;
4172
				}
4173
				// Was it the last step in the file?
4174
				else if (bIsComplete && iDebugNum == -1)
4175
				{
4176
					lastItem = 0;
4177
					prevFile = curFile;';
4178
4179
		if ($is_debug)
4180
			echo '
4181
					setOuterHTML(document.getElementById(\'debuginfo\'), \'Moving to next script file...done<br><span id="debuginfo"><\' + \'/span>\');';
4182
4183
		echo '
4184
					getNextItem();
4185
					return true;
4186
				}';
4187
4188
		// If debug scroll the screen.
4189
		if ($is_debug)
4190
			echo '
4191
				if (iLastSubStepProgress == -1)
4192
				{
4193
					// Give it consistent dots.
4194
					dots = sDebugName.match(/\./g);
4195
					numDots = dots ? dots.length : 0;
4196
					for (var i = numDots; i < 3; i++)
4197
						sDebugName += ".";
4198
					setOuterHTML(document.getElementById(\'debuginfo\'), sDebugName + \'<span id="debuginfo"><\' + \'/span>\');
4199
				}
4200
				iLastSubStepProgress = iSubStepProgress;
4201
4202
				if (bIsComplete)
4203
					setOuterHTML(document.getElementById(\'debuginfo\'), \'done<br><span id="debuginfo"><\' + \'/span>\');
4204
				else
4205
					setOuterHTML(document.getElementById(\'debuginfo\'), \'...<span id="debuginfo"><\' + \'/span>\');
4206
4207
				if (document.getElementById(\'debug_section\').scrollHeight)
4208
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4209
4210
		echo '
4211
				// Update the page.
4212
				setInnerHTML(document.getElementById(\'item_num\'), iItemNum);
4213
				setInnerHTML(document.getElementById(\'cur_item_name\'), sItemName);';
4214
4215
		if ($upcontext['file_count'] > 1)
4216
		{
4217
			echo '
4218
				setInnerHTML(document.getElementById(\'file_done\'), curFile);
4219
				setInnerHTML(document.getElementById(\'item_count\'), totalItems);';
4220
		}
4221
4222
		echo '
4223
				// Is there an error?
4224
				if (oXMLDoc.getElementsByTagName("error")[0])
4225
				{
4226
					var sErrorMsg = "";
4227
					for (var i = 0; i < oXMLDoc.getElementsByTagName("error")[0].childNodes.length; i++)
4228
						sErrorMsg += oXMLDoc.getElementsByTagName("error")[0].childNodes[i].nodeValue;
4229
					document.getElementById("error_block").style.display = "";
4230
					setInnerHTML(document.getElementById("error_message"), sErrorMsg);
4231
					return false;
4232
				}
4233
4234
				// Get the progress bar right.
4235
				barTotal = debugItems * ', $upcontext['file_count'], ';
4236
				barDone = (debugItems * (curFile - 1)) + lastItem;
4237
4238
				updateStepProgress(barDone, barTotal, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4239
4240
				// Finally - update the time here as it shows the server is responding!
4241
				curTime = new Date();
4242
				iElapsed = (curTime.getTime() / 1000 - ', $upcontext['started'], ');
4243
				mins = parseInt(iElapsed / 60);
4244
				secs = parseInt(iElapsed - mins * 60);
4245
				setInnerHTML(document.getElementById("mins_elapsed"), mins);
4246
				setInnerHTML(document.getElementById("secs_elapsed"), secs);
4247
4248
				getNextItem();
4249
				return true;
4250
			}
4251
4252
			// What if we timeout?!
4253
			function retTimeout(attemptAgain)
4254
			{
4255
				// Oh noes...
4256
				if (!attemptAgain)
4257
				{
4258
					document.getElementById("error_block").style.display = "";
4259
					setInnerHTML(document.getElementById("error_message"), "Server has not responded for ', $timeLimitThreshold, ' seconds. It may be worth waiting a little longer or otherwise please click <a href=\"#\" onclick=\"retTimeout(true); return false;\">here<" + "/a> to try this step again");
4260
				}
4261
				else
4262
				{
4263
					document.getElementById("error_block").style.display = "none";
4264
					getNextItem();
4265
				}
4266
			}';
4267
4268
		// Start things off assuming we've not errored.
4269
		if (empty($upcontext['error_message']))
4270
			echo '
4271
			getNextItem();';
4272
4273
		echo '
4274
		//# sourceURL=dynamicScript-dbch.js
4275
		</script>';
4276
	}
4277
	return;
4278
}
4279
4280
function template_database_xml()
4281
{
4282
	global $is_debug, $upcontext;
4283
4284
	echo '
4285
	<file num="', $upcontext['cur_file_num'], '" items="', $upcontext['total_items'], '" debug_items="', $upcontext['debug_items'], '">', $upcontext['cur_file_name'], '</file>
4286
	<item num="', $upcontext['current_item_num'], '">', $upcontext['current_item_name'], '</item>
4287
	<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>';
4288
4289
	if (!empty($upcontext['error_message']))
4290
		echo '
4291
	<error>', $upcontext['error_message'], '</error>';
4292
4293
	if (!empty($upcontext['error_string']))
4294
		echo '
4295
	<sql>', $upcontext['error_string'], '</sql>';
4296
4297
	if ($is_debug)
4298
		echo '
4299
	<curtime>', time(), '</curtime>';
4300
}
4301
4302
// Template for the UTF-8 conversion step. Basically a copy of the backup stuff with slight modifications....
4303
function template_convert_utf8()
4304
{
4305
	global $upcontext, $support_js, $is_debug;
4306
4307
	echo '
4308
			<h3>Please wait while your database is converted to UTF-8. For large forums this may take some time!</h3>';
4309
4310
	echo '
4311
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4312
			<input type="hidden" name="utf8_done" id="utf8_done" value="0">
4313
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
4314
			<div id="debug_section" style="height: ', ($is_debug ? '97' : '12') , 'px; overflow: auto;">
4315
			<span id="debuginfo"></span>
4316
			</div>';
4317
4318
	// Done any tables so far?
4319
	if (!empty($upcontext['previous_tables']))
4320
		foreach ($upcontext['previous_tables'] as $table)
4321
			echo '
4322
			<br>Completed Table: &quot;', $table, '&quot;.';
4323
4324
	echo '
4325
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>';
4326
4327
	// If we dropped their index, let's let them know
4328
	if ($upcontext['dropping_index'])
4329
		echo '
4330
				<br><span id="indexmsg" style="font-weight: bold; font-style: italic; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">Please note that your fulltext index was dropped to facilitate the conversion and will need to be recreated in the admin area after the upgrade is complete.</span>';
4331
4332
	// Completion notification
4333
	echo '
4334
			<br><span id="commess" style="font-weight: bold; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">Conversion Complete! Click Continue to Proceed.</span>';
4335
4336
	// Continue please!
4337
	$upcontext['continue'] = $support_js ? 2 : 1;
4338
4339
	// If javascript allows we want to do this using XML.
4340
	if ($support_js)
4341
	{
4342
		echo '
4343
		<script>
4344
			var lastTable = ', $upcontext['cur_table_num'], ';
4345
			function getNextTables()
4346
			{
4347
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onConversionUpdate);
4348
			}
4349
4350
			// Got an update!
4351
			function onConversionUpdate(oXMLDoc)
4352
			{
4353
				var sCurrentTableName = "";
4354
				var iTableNum = 0;
4355
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4356
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4357
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4358
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4359
4360
				// Update the page.
4361
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4362
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4363
				lastTable = iTableNum;
4364
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4365
4366
		// If debug flood the screen.
4367
		if ($is_debug)
4368
			echo '
4369
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4370
4371
				if (document.getElementById(\'debug_section\').scrollHeight)
4372
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4373
4374
		echo '
4375
				// Get the next update...
4376
				if (iTableNum == ', $upcontext['table_count'], ')
4377
				{
4378
					document.getElementById(\'commess\').style.display = "";
4379
					if (document.getElementById(\'indexmsg\') != null) {
4380
						document.getElementById(\'indexmsg\').style.display = "";
4381
					}
4382
					document.getElementById(\'current_tab_div\').style.display = "none";
4383
					document.getElementById(\'contbutt\').disabled = 0;
4384
					document.getElementById(\'utf8_done\').value = 1;
4385
				}
4386
				else
4387
					getNextTables();
4388
			}
4389
			getNextTables();
4390
		//# sourceURL=dynamicScript-conv.js
4391
		</script>';
4392
	}
4393
}
4394
4395
function template_convert_xml()
4396
{
4397
	global $upcontext;
4398
4399
	echo '
4400
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4401
}
4402
4403
// Template for the database backup tool/
4404
function template_serialize_json()
4405
{
4406
	global $upcontext, $support_js, $is_debug;
4407
4408
	echo '
4409
			<h3>Converting data from serialize to JSON...</h3>';
4410
4411
	echo '
4412
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4413
			<input type="hidden" name="json_done" id="json_done" value="0">
4414
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
4415
			<div id="debug_section" style="height: ', ($is_debug ? '115' : '12') , 'px; overflow: auto;">
4416
			<span id="debuginfo"></span>
4417
			</div>';
4418
4419
	// Dont any tables so far?
4420
	if (!empty($upcontext['previous_tables']))
4421
		foreach ($upcontext['previous_tables'] as $table)
4422
			echo '
4423
			<br>Completed Table: &quot;', $table, '&quot;.';
4424
4425
	echo '
4426
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
4427
			<br><span id="commess" style="font-weight: bold; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">Convert to JSON Complete! Click Continue to Proceed.</span>';
4428
4429
	// Try to make sure substep was reset.
4430
	if ($upcontext['cur_table_num'] == $upcontext['table_count'])
4431
		echo '
4432
			<input type="hidden" name="substep" id="substep" value="0">';
4433
4434
	// Continue please!
4435
	$upcontext['continue'] = $support_js ? 2 : 1;
4436
4437
	// If javascript allows we want to do this using XML.
4438
	if ($support_js)
4439
	{
4440
		echo '
4441
		<script>
4442
			var lastTable = ', $upcontext['cur_table_num'], ';
4443
			function getNextTables()
4444
			{
4445
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4446
			}
4447
4448
			// Got an update!
4449
			function onBackupUpdate(oXMLDoc)
4450
			{
4451
				var sCurrentTableName = "";
4452
				var iTableNum = 0;
4453
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4454
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4455
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4456
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4457
4458
				// Update the page.
4459
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4460
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4461
				lastTable = iTableNum;
4462
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4463
4464
		// If debug flood the screen.
4465
		if ($is_debug)
4466
			echo '
4467
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4468
4469
				if (document.getElementById(\'debug_section\').scrollHeight)
4470
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4471
4472
		echo '
4473
				// Get the next update...
4474
				if (iTableNum == ', $upcontext['table_count'], ')
4475
				{
4476
					document.getElementById(\'commess\').style.display = "";
4477
					document.getElementById(\'current_tab_div\').style.display = "none";
4478
					document.getElementById(\'contbutt\').disabled = 0;
4479
					document.getElementById(\'json_done\').value = 1;
4480
				}
4481
				else
4482
					getNextTables();
4483
			}
4484
			getNextTables();
4485
		//# sourceURL=dynamicScript-json.js
4486
		</script>';
4487
	}
4488
}
4489
4490
function template_serialize_json_xml()
4491
{
4492
	global $upcontext;
4493
4494
	echo '
4495
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4496
}
4497
4498
function template_upgrade_complete()
4499
{
4500
	global $upcontext, $upgradeurl, $settings, $boardurl, $is_debug;
4501
4502
	echo '
4503
	<h3>That wasn\'t so hard, was it?  Now you are ready to use <a href="', $boardurl, '/index.php">your installation of SMF</a>.  Hope you like it!</h3>
4504
	<form action="', $boardurl, '/index.php">';
4505
4506
	if (!empty($upcontext['can_delete_script']))
4507
		echo '
4508
			<label for="delete_self"><input type="checkbox" id="delete_self" onclick="doTheDelete(this);" class="input_check"> Delete upgrade.php and its data files now</label> <em>(doesn\'t work on all servers).</em>
4509
			<script>
4510
				function doTheDelete(theCheck)
4511
				{
4512
					var theImage = document.getElementById ? document.getElementById("delete_upgrader") : document.all.delete_upgrader;
4513
4514
					theImage.src = "', $upgradeurl, '?delete=1&ts_" + (new Date().getTime());
4515
					theCheck.disabled = true;
4516
				}
4517
			</script>
4518
			<img src="', $settings['default_theme_url'], '/images/blank.png" alt="" id="delete_upgrader"><br>';
4519
4520
	$active = time() - $upcontext['started'];
4521
	$hours = floor($active / 3600);
4522
	$minutes = intval(($active / 60) % 60);
4523
	$seconds = intval($active % 60);
4524
4525
	if ($is_debug)
4526
	{
4527
		$totalTime = '';
4528
		if ($hours > 0)
4529
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
4530
		if ($minutes > 0)
4531
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
4532
		if ($seconds > 0)
4533
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
4534
	}
4535
4536
	if ($is_debug && !empty($totalTime))
4537
		echo '<br> Upgrade completed in ', $totalTime, '<br><br>';
4538
4539
	echo '<br>
4540
			If you had any problems with this upgrade, or have any problems using SMF, please don\'t hesitate to <a href="https://www.simplemachines.org/community/index.php">look to us for assistance</a>.<br>
4541
			<br>
4542
			Best of luck,<br>
4543
			Simple Machines';
4544
}
4545
4546
/**
4547
 * Convert MySQL (var)char ip col to binary
4548
 *
4549
 * @param string $targetTable The table to perform the operation on
4550
 * @param string $oldCol The old column to gather data from
4551
 * @param string $newCol The new column to put data in
4552
 * @param int $limit The amount of entries to handle at once.
4553
 * @param int $setSize The amount of entries after which to update the database.
4554
 *
4555
 * newCol needs to be a varbinary(16) null able field
4556
 * @return bool
4557
 */
4558
function MySQLConvertOldIp($targetTable, $oldCol, $newCol, $limit = 50000, $setSize = 100)
4559
{
4560
	global $smcFunc, $step_progress;
4561
4562
	$current_substep = $_GET['substep'];
4563
4564
	if (empty($_GET['a']))
4565
		$_GET['a'] = 0;
4566
	$step_progress['name'] = 'Converting ips';
4567
	$step_progress['current'] = $_GET['a'];
4568
4569
	// Skip this if we don't have the column
4570
	$request = $smcFunc['db_query']('', '
4571
		SHOW FIELDS
4572
		FROM {db_prefix}{raw:table}
4573
		WHERE Field = {string:name}',
4574
		array(
4575
			'table' => $targetTable,
4576
			'name' => $oldCol,
4577
	));
4578
	if ($smcFunc['db_num_rows']($request) !== 1)
4579
	{
4580
		$smcFunc['db_free_result']($request);
4581
		return;
4582
	}
4583
	$smcFunc['db_free_result']($request);
4584
4585
	//mysql default max length is 1mb https://dev.mysql.com/doc/refman/5.1/en/packet-too-large.html
4586
	$arIp = array();
0 ignored issues
show
$arIp is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
4587
4588
	$is_done = false;
4589
	while (!$is_done)
4590
	{
4591
		// Keep looping at the current step.
4592
		nextSubStep($current_substep);
4593
4594
		$arIp = array();
4595
4596
		$request = $smcFunc['db_query']('', '
4597
			SELECT DISTINCT {raw:old_col}
4598
			FROM {db_prefix}{raw:table_name}
4599
			WHERE {raw:new_col} IS NULL
4600
			LIMIT {int:limit}',
4601
			array(
4602
				'old_col' => $oldCol,
4603
				'new_col' => $newCol,
4604
				'table_name' => $targetTable,
4605
				'empty' => '',
4606
				'limit' => $limit,
4607
		));
4608
		while ($row = $smcFunc['db_fetch_assoc']($request))
4609
			$arIp[] = $row[$oldCol];
4610
		$smcFunc['db_free_result']($request);
4611
4612
		// Special case, null ip could keep us in a loop.
4613
		if (is_null($arIp[0]))
4614
			unset($arIp[0]);
4615
4616
		if (empty($arIp))
4617
			$is_done = true;
4618
4619
		$updates = array();
4620
		$cases = array();
4621
		$count = count($arIp);
4622
		for ($i = 0; $i < $count; $i++)
4623
		{
4624
			$arIp[$i] = trim($arIp[$i]);
4625
4626
			if (empty($arIp[$i]))
4627
				continue;
4628
4629
			$updates['ip' . $i] = $arIp[$i];
4630
			$cases[$arIp[$i]] = 'WHEN ' . $oldCol . ' = {string:ip' . $i . '} THEN {inet:ip' . $i . '}';
4631
4632
			if ($setSize > 0 && $i % $setSize === 0)
4633
			{
4634
				if (count($updates) == 1)
4635
					continue;
4636
4637
				$updates['whereSet'] = array_values($updates);
4638
				$smcFunc['db_query']('', '
4639
					UPDATE {db_prefix}' . $targetTable . '
4640
					SET ' . $newCol . ' = CASE ' .
4641
					implode('
4642
						', $cases) . '
4643
						ELSE NULL
4644
					END
4645
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4646
					$updates
4647
				);
4648
4649
				$updates = array();
4650
				$cases = array();
4651
			}
4652
		}
4653
4654
		// Incase some extras made it through.
4655
		if (!empty($updates))
4656
		{
4657
			if (count($updates) == 1)
4658
			{
4659
				foreach ($updates as $key => $ip)
4660
				{
4661
					$smcFunc['db_query']('', '
4662
						UPDATE {db_prefix}' . $targetTable . '
4663
						SET ' . $newCol . ' = {inet:ip}
4664
						WHERE ' . $oldCol . ' = {string:ip}',
4665
						array(
4666
							'ip' => $ip
4667
					));
4668
				}
4669
			}
4670
			else
4671
			{
4672
				$updates['whereSet'] = array_values($updates);
4673
				$smcFunc['db_query']('', '
4674
					UPDATE {db_prefix}' . $targetTable . '
4675
					SET ' . $newCol . ' = CASE ' .
4676
					implode('
4677
						', $cases) . '
4678
						ELSE NULL
4679
					END
4680
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4681
					$updates
4682
				);
4683
			}
4684
		}
4685
		else
4686
			$is_done = true;
4687
4688
		$_GET['a'] += $limit;
4689
		$step_progress['current'] = $_GET['a'];
4690
	}
4691
4692
	unset($_GET['a']);
4693
}
4694
4695
/**
4696
 * Get the column info. This is basically the same as smf_db_list_columns but we get 1 column, force detail and other checks.
4697
 *
4698
 * @param string $targetTable The table to perform the operation on
4699
 * @param string $column The column we are looking for.
4700
 *
4701
 * @return array Info on the table.
4702
 */
4703
function upgradeGetColumnInfo($targetTable, $column)
4704
{
4705
 	global $smcFunc;
4706
4707
 	// This should already be here, but be safe.
4708
 	db_extend('packages');
4709
4710
 	$columns = $smcFunc['db_list_columns']($targetTable, true);
4711
4712
	if (isset($columns[$column]))
4713
		return $columns[$column];
4714
	else
4715
		return null;
4716
}
4717
4718
?>
0 ignored issues
show
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...