Completed
Push — release-2.1 ( 001348...ca7b71 )
by Mathias
18:55
created

upgrade.php ➔ template_convert_xml()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
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 3
12
 */
13
14
// Version information...
15
define('SMF_VERSION', '2.1 Beta 3');
16
define('SMF_LANG_VERSION', '2.1 Beta 3');
17
18
/**
19
 * The minimum required PHP version.
20
 * @var string
21
 */
22
$GLOBALS['required_php_version'] = '5.3.8';
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
Duplication introduced by
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
Duplication introduced by
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
Duplication introduced by
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
Duplication introduced by
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
			$_GET['substep'] = 0;
284
			$upcontext['current_step']++;
285
		}
286
	}
287
	$upcontext['overall_percent'] += $step[3];
288
}
289
290
upgradeExit();
291
292
// Exit the upgrade script.
293
function upgradeExit($fallThrough = false)
294
{
295
	global $upcontext, $upgradeurl, $sourcedir, $command_line, $is_debug;
296
297
	// Save where we are...
298
	if (!empty($upcontext['current_step']) && !empty($upcontext['user']['id']))
299
	{
300
		$upcontext['user']['step'] = $upcontext['current_step'];
301
		$upcontext['user']['substep'] = $_GET['substep'];
302
		$upcontext['user']['updated'] = time();
303
		$upcontext['debug'] = $is_debug;
304
		$upgradeData = base64_encode(json_encode($upcontext['user']));
305
		require_once($sourcedir . '/Subs-Admin.php');
306
		updateSettingsFile(array('upgradeData' => '"' . $upgradeData . '"'));
307
		updateDbLastError(0);
0 ignored issues
show
Unused Code introduced by
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...
308
	}
309
310
	// Handle the progress of the step, if any.
311
	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
312
	{
313
		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
314
		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
315
	}
316
	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
317
318
	// We usually dump our templates out.
319
	if (!$fallThrough)
320
	{
321
		// This should not happen my dear... HELP ME DEVELOPERS!!
322
		if (!empty($command_line))
323
		{
324
			if (function_exists('debug_print_backtrace'))
325
				debug_print_backtrace();
326
327
			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.';
328
			flush();
329
			die();
330
		}
331
332
		if (!isset($_GET['xml']))
333
			template_upgrade_above();
334
		else
335
		{
336
			header('Content-Type: text/xml; charset=UTF-8');
337
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
338
			$upcontext['get_data'] = array();
339
			foreach ($_GET as $k => $v)
340
			{
341
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
342
				{
343
					$upcontext['get_data'][$k] = $v;
344
				}
345
			}
346
			template_xml_above();
347
		}
348
349
		// Call the template.
350
		if (isset($upcontext['sub_template']))
351
		{
352
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
353
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(json_encode($upcontext['upgrade_status']));
354
355
			// Custom stuff to pass back?
356
			if (!empty($upcontext['query_string']))
357
				$upcontext['form_url'] .= $upcontext['query_string'];
358
359
			// Call the appropriate subtemplate
360
			if (is_callable('template_' . $upcontext['sub_template']))
361
				call_user_func('template_' . $upcontext['sub_template']);
362
			else
363
				die('Upgrade aborted!  Invalid template: template_' . $upcontext['sub_template']);
364
		}
365
366
		// Was there an error?
367
		if (!empty($upcontext['forced_error_message']))
368
			echo $upcontext['forced_error_message'];
369
370
		// Show the footer.
371
		if (!isset($_GET['xml']))
372
			template_upgrade_below();
373
		else
374
			template_xml_below();
375
	}
376
377
378
	if (!empty($command_line) && $is_debug)
379
	{
380
		$active = time() - $upcontext['started'];
381
		$hours = floor($active / 3600);
382
		$minutes = intval(($active / 60) % 60);
383
		$seconds = intval($active % 60);
384
385
		$totalTime = '';
386
		if ($hours > 0)
387
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
388
		if ($minutes > 0)
389
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
390
		if ($seconds > 0)
391
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
392
393
		if (!empty($totalTime))
394
			echo "\n" . 'Upgrade completed in ' . $totalTime . "\n";
395
	}
396
397
	// Bang - gone!
398
	die();
399
}
400
401
// Used to direct the user to another location.
402
function redirectLocation($location, $addForm = true)
403
{
404
	global $upgradeurl, $upcontext, $command_line;
405
406
	// Command line users can't be redirected.
407
	if ($command_line)
408
		upgradeExit(true);
409
410
	// Are we providing the core info?
411
	if ($addForm)
412
	{
413
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
414
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
415
	}
416
417
	while (@ob_end_clean());
418
	header('Location: ' . strtr($location, array('&amp;' => '&')));
419
420
	// Exit - saving status as we go.
421
	upgradeExit(true);
422
}
423
424
// Load all essential data and connect to the DB as this is pre SSI.php
425
function loadEssentialData()
426
{
427
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection, $db_prefix, $db_character_set, $db_type;
428
	global $modSettings, $sourcedir, $smcFunc;
429
430
	// Do the non-SSI stuff...
431
	if (function_exists('set_magic_quotes_runtime'))
432
		@set_magic_quotes_runtime(0);
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...
433
434
	error_reporting(E_ALL);
435
	define('SMF', 1);
436
437
	// Start the session.
438
	if (@ini_get('session.save_handler') == 'user')
439
		@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...
440
	@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...
441
442
	if (empty($smcFunc))
443
		$smcFunc = array();
444
445
	// We need this for authentication and some upgrade code
446
	require_once($sourcedir . '/Subs-Auth.php');
447
	require_once($sourcedir . '/Class-Package.php');
448
449
	$smcFunc['strtolower'] = 'smf_strtolower';
450
451
	// Initialize everything...
452
	initialize_inputs();
453
454
	// Get the database going!
455
	if (empty($db_type) || $db_type == 'mysqli')
456
		$db_type = 'mysql';
457
458
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
459
	{
460
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
461
462
		// Make the connection...
463
		if (empty($db_connection))
464
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, array('non_fatal' => true));
465
		else
466
			// If we've returned here, ping/reconnect to be safe
467
			$smcFunc['db_ping']($db_connection);
468
469
		// Oh dear god!!
470
		if ($db_connection === null)
471
			die('Unable to connect to database - please check username and password are correct in Settings.php');
472
473
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
474
			$smcFunc['db_query']('', '
475
			SET NAMES {string:db_character_set}',
476
			array(
477
				'db_error_skip' => true,
478
				'db_character_set' => $db_character_set,
479
			)
480
		);
481
482
		// Load the modSettings data...
483
		$request = $smcFunc['db_query']('', '
484
			SELECT variable, value
485
			FROM {db_prefix}settings',
486
			array(
487
				'db_error_skip' => true,
488
			)
489
		);
490
		$modSettings = array();
491 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Duplication introduced by
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...
492
			$modSettings[$row['variable']] = $row['value'];
493
		$smcFunc['db_free_result']($request);
494
	}
495
	else
496
	{
497
		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.');
498
	}
499
500
	require_once($sourcedir . '/Subs.php');
501
502
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
503
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
504
	{
505
		require_once($sourcedir . '/QueryString.php');
506
		cleanRequest();
507
	}
508
509
	if (!isset($_GET['substep']))
510
		$_GET['substep'] = 0;
511
}
512
513
function initialize_inputs()
0 ignored issues
show
Best Practice introduced by
The function initialize_inputs() has been defined more than once; this definition is ignored, only the first definition in other/install.php (L141-255) 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...
514
{
515
	global $start_time, $upcontext, $db_type;
516
517
	$start_time = time();
518
519
	umask(0);
520
521
	ob_start();
522
523
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
524
	ignore_user_abort(true);
525
526
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
527
	if (isset($_GET['delete']))
528
	{
529
		@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...
530
531
		// And the extra little files ;).
532
		@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...
533
		@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...
534
		@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...
535
		@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...
536
		@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...
537
538
		$dh = opendir(dirname(__FILE__));
539
		while ($file = readdir($dh))
540
		{
541
			if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
542
				@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...
543
		}
544
		closedir($dh);
545
546
		// Legacy files while we're at it. NOTE: We only touch files we KNOW shouldn't be there.
547
		// 1.1 Sources files not in 2.0+
548
		@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...
549
		// 1.1 Templates that don't exist any more (e.g. renamed)
550
		@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...
551
		@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...
552
		// 1.1 JS files were stored in the main theme folder, but in 2.0+ are in the scripts/ folder
553
		@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...
554
		@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...
555
		@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...
556
		@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...
557
		@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...
558
559
		// 2.0 Sources files not in 2.1+
560
		@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...
561
		@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...
562
563
		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');
564
		exit;
565
	}
566
567
	// Something is causing this to happen, and it's annoying.  Stop it.
568
	$temp = 'upgrade_php?step';
569
	while (strlen($temp) > 4)
570
	{
571
		if (isset($_GET[$temp]))
572
			unset($_GET[$temp]);
573
		$temp = substr($temp, 1);
574
	}
575
576
	// Force a step, defaulting to 0.
577
	$_GET['step'] = (int) @$_GET['step'];
578
	$_GET['substep'] = (int) @$_GET['substep'];
579
}
580
581
// Step 0 - Let's welcome them in and ask them to login!
582
function WelcomeLogin()
583
{
584
	global $boarddir, $sourcedir, $modSettings, $cachedir, $upgradeurl, $upcontext;
585
	global $smcFunc, $db_type, $databases, $boardurl;
586
587
	// We global $txt here so that the language files can add to them. This variable is NOT unused.
588
	global $txt;
589
590
	$upcontext['sub_template'] = 'welcome_message';
591
592
	// Check for some key files - one template, one language, and a new and an old source file.
593
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
594
		&& @file_exists($sourcedir . '/QueryString.php')
595
		&& @file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')
596
		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
597
598
	// Need legacy scripts?
599 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.1)
0 ignored issues
show
Duplication introduced by
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_2-0_' . $db_type . '.sql');
601 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.0)
0 ignored issues
show
Duplication introduced by
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-1.sql');
603 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 1.1)
0 ignored issues
show
Duplication introduced by
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...
604
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
605
606
	// We don't need "-utf8" files anymore...
607
	$upcontext['language'] = str_ireplace('-utf8', '', $upcontext['language']);
608
609
	// This needs to exist!
610
	if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
611
		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>]');
612
	else
613
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
614
615
	if (!$check)
616
		// 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.
617
		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.');
618
619
	// Do they meet the install requirements?
620
	if (!php_version_check())
621
		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.');
622
623
	if (!db_version_check())
624
		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.');
625
626
	// Do some checks to make sure they have proper privileges
627
	db_extend('packages');
628
629
	// CREATE
630
	$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');
631
632
	// ALTER
633
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
634
635
	// DROP
636
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
637
638
	// Sorry... we need CREATE, ALTER and DROP
639 View Code Duplication
	if (!$create || !$alter || !$drop)
0 ignored issues
show
Duplication introduced by
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...
640
		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.');
641
642
	// Do a quick version spot check.
643
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
644
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
645
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
646
		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.');
647
648
	// What absolutely needs to be writable?
649
	$writable_files = array(
650
		$boarddir . '/Settings.php',
651
		$boarddir . '/Settings_bak.php',
652
		$boarddir . '/db_last_error.php',
653
		$modSettings['theme_dir'] . '/css/minified.css',
654
		$modSettings['theme_dir'] . '/scripts/minified.js',
655
		$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
656
	);
657
658
	// Do we need to add this setting?
659
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
660
661
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] . '/custom_avatar';
662
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
663
664
	// This little fellow has to cooperate...
665
	quickFileWritable($custom_av_dir);
666
667
	// Are we good now?
668
	if (!is_writable($custom_av_dir))
669
		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));
670
	elseif ($need_settings_update)
671
	{
672
		if (!function_exists('cache_put_data'))
673
			require_once($sourcedir . '/Load.php');
674
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
675
		updateSettings(array('custom_avatar_url' => $custom_av_url));
676
	}
677
678
	require_once($sourcedir . '/Security.php');
679
680
	// Check the cache directory.
681
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
682
	if (!file_exists($cachedir_temp))
683
		@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...
684
	if (!file_exists($cachedir_temp))
685
		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.');
686
687
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
688
		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>.');
689
	elseif (!isset($_GET['skiplang']))
690
	{
691
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
692
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
693
694
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
695
			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>]');
696
	}
697
698
	if (!makeFilesWritable($writable_files))
699
		return false;
700
701
	// Check agreement.txt. (it may not exist, in which case $boarddir must be writable.)
702 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
0 ignored issues
show
Duplication introduced by
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...
703
		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.');
704
705
	// Upgrade the agreement.
706
	elseif (isset($modSettings['agreement']))
707
	{
708
		$fp = fopen($boarddir . '/agreement.txt', 'w');
709
		fwrite($fp, $modSettings['agreement']);
710
		fclose($fp);
711
	}
712
713
	// We're going to check that their board dir setting is right in case they've been moving stuff around.
714
	if (strtr($boarddir, array('/' => '', '\\' => '')) != strtr(dirname(__FILE__), array('/' => '', '\\' => '')))
715
		$upcontext['warning'] = '
716
			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>
717
			<ul>
718
				<li>Board Directory: ' . $boarddir . '</li>
719
				<li>Source Directory: ' . $boarddir . '</li>
720
				<li>Cache Directory: ' . $cachedir_temp . '</li>
721
			</ul>
722
			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.';
723
724
	// Either we're logged in or we're going to present the login.
725
	if (checkLogin())
726
		return true;
727
728
	$upcontext += createToken('login');
729
730
	return false;
731
}
732
733
// Step 0.5: Does the login work?
734
function checkLogin()
735
{
736
	global $modSettings, $upcontext, $disable_security;
737
	global $smcFunc, $db_type, $support_js;
738
739
	// Don't bother if the security is disabled.
740
	if ($disable_security)
741
		return true;
742
743
	// Are we trying to login?
744
	if (isset($_POST['contbutt']) && (!empty($_POST['user'])))
745
	{
746
		// If we've disabled security pick a suitable name!
747
		if (empty($_POST['user']))
748
			$_POST['user'] = 'Administrator';
749
750
		// Before 2.0 these column names were different!
751
		$oldDB = false;
752
		if (empty($db_type) || $db_type == 'mysql')
753
		{
754
			$request = $smcFunc['db_query']('', '
755
				SHOW COLUMNS
756
				FROM {db_prefix}members
757
				LIKE {string:member_name}',
758
				array(
759
					'member_name' => 'memberName',
760
					'db_error_skip' => true,
761
				)
762
			);
763
			if ($smcFunc['db_num_rows']($request) != 0)
764
				$oldDB = true;
765
			$smcFunc['db_free_result']($request);
766
		}
767
768
		// Get what we believe to be their details.
769
		if (!$disable_security)
770
		{
771
			if ($oldDB)
772
				$request = $smcFunc['db_query']('', '
773
					SELECT id_member, memberName AS member_name, passwd, id_group,
774
					additionalGroups AS additional_groups, lngfile
775
					FROM {db_prefix}members
776
					WHERE memberName = {string:member_name}',
777
					array(
778
						'member_name' => $_POST['user'],
779
						'db_error_skip' => true,
780
					)
781
				);
782
			else
783
				$request = $smcFunc['db_query']('', '
784
					SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile
785
					FROM {db_prefix}members
786
					WHERE member_name = {string:member_name}',
787
					array(
788
						'member_name' => $_POST['user'],
789
						'db_error_skip' => true,
790
					)
791
				);
792
			if ($smcFunc['db_num_rows']($request) != 0)
793
			{
794
				list ($id_member, $name, $password, $id_group, $addGroups, $user_language) = $smcFunc['db_fetch_row']($request);
795
796
				$groups = explode(',', $addGroups);
797
				$groups[] = $id_group;
798
799
				foreach ($groups as $k => $v)
800
					$groups[$k] = (int) $v;
801
802
				$sha_passwd = sha1(strtolower($name) . un_htmlspecialchars($_REQUEST['passwrd']));
803
804
				// We don't use "-utf8" anymore...
805
				$user_language = str_ireplace('-utf8', '', $user_language);
806
			}
807
			else
808
				$upcontext['username_incorrect'] = true;
809
			$smcFunc['db_free_result']($request);
810
		}
811
		$upcontext['username'] = $_POST['user'];
812
813
		// Track whether javascript works!
814
		if (!empty($_POST['js_works']))
815
		{
816
			$upcontext['upgrade_status']['js'] = 1;
817
			$support_js = 1;
818
		}
819
		else
820
			$support_js = 0;
821
822
		// Note down the version we are coming from.
823
		if (!empty($modSettings['smfVersion']) && empty($upcontext['user']['version']))
824
			$upcontext['user']['version'] = $modSettings['smfVersion'];
825
826
		// Didn't get anywhere?
827
		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
Bug introduced by
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...
828
		{
829
			// MD5?
830
			$md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user']));
831
			if ($md5pass != $password)
0 ignored issues
show
Bug introduced by
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...
832
			{
833
				$upcontext['password_failed'] = true;
834
				// Disable the hashing this time.
835
				$upcontext['disable_login_hashing'] = true;
836
			}
837
		}
838
839
		if ((empty($upcontext['password_failed']) && !empty($name)) || $disable_security)
840
		{
841
			// Set the password.
842
			if (!$disable_security)
843
			{
844
				// Do we actually have permission?
845
				if (!in_array(1, $groups))
846
				{
847
					$request = $smcFunc['db_query']('', '
848
						SELECT permission
849
						FROM {db_prefix}permissions
850
						WHERE id_group IN ({array_int:groups})
851
							AND permission = {string:admin_forum}',
852
						array(
853
							'groups' => $groups,
0 ignored issues
show
Bug introduced by
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...
854
							'admin_forum' => 'admin_forum',
855
							'db_error_skip' => true,
856
						)
857
					);
858
					if ($smcFunc['db_num_rows']($request) == 0)
859
						return throw_error('You need to be an admin to perform an upgrade!');
860
					$smcFunc['db_free_result']($request);
861
				}
862
863
				$upcontext['user']['id'] = $id_member;
0 ignored issues
show
Bug introduced by
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...
864
				$upcontext['user']['name'] = $name;
865
			}
866
			else
867
			{
868
				$upcontext['user']['id'] = 1;
869
				$upcontext['user']['name'] = 'Administrator';
870
			}
871
			$upcontext['user']['pass'] = mt_rand(0, 60000);
872
			// This basically is used to match the GET variables to Settings.php.
873
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
874
875
			// Set the language to that of the user?
876
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
877
			{
878
				$user_language = basename($user_language, '.lng');
879
				$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
880
				preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
881
882
				if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
883
					$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'] . '.';
884
				elseif (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . basename($user_language, '.lng') . '.php'))
885
					$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'] . '.';
886
				else
887
				{
888
					// Set this as the new language.
889
					$upcontext['language'] = $user_language;
890
					$upcontext['upgrade_status']['lang'] = $upcontext['language'];
891
892
					// Include the file.
893
					require_once($modSettings['theme_dir'] . '/languages/Install.' . $user_language . '.php');
894
				}
895
			}
896
897
			// If we're resuming set the step and substep to be correct.
898
			if (isset($_POST['cont']))
899
			{
900
				$upcontext['current_step'] = $upcontext['user']['step'];
901
				$_GET['substep'] = $upcontext['user']['substep'];
902
			}
903
904
			return true;
905
		}
906
	}
907
908
	return false;
909
}
910
911
// Step 1: Do the maintenance and backup.
912
function UpgradeOptions()
913
{
914
	global $db_prefix, $command_line, $modSettings, $is_debug, $smcFunc, $packagesdir, $tasksdir, $language;
915
	global $boarddir, $boardurl, $sourcedir, $maintenance, $cachedir, $upcontext, $db_type, $db_server, $db_last_error;
916
917
	$upcontext['sub_template'] = 'upgrade_options';
918
	$upcontext['page_title'] = 'Upgrade Options';
919
920
	db_extend('packages');
921
	$upcontext['karma_installed'] = array('good' => false, 'bad' => false);
922
	$member_columns = $smcFunc['db_list_columns']('{db_prefix}members');
923
924
	$upcontext['karma_installed']['good'] = in_array('karma_good', $member_columns);
925
	$upcontext['karma_installed']['bad'] = in_array('karma_bad', $member_columns);
926
927
	unset($member_columns);
928
929
	// If we've not submitted then we're done.
930
	if (empty($_POST['upcont']))
931
		return false;
932
933
	// Firstly, if they're enabling SM stat collection just do it.
934
	if (!empty($_POST['stats']) && (substr($boardurl, 0, 16) != 'http://localhost' || substr($boardurl, 0, 16) != 'https://localhost') && empty($modSettings['allow_sm_stats']))
935
	{
936
		// Attempt to register the site etc.
937
		$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
938
		if ($fp)
939
		{
940
			$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
941
			$out .= 'Host: www.simplemachines.org' . "\r\n";
942
			$out .= 'Connection: Close' . "\r\n\r\n";
943
			fwrite($fp, $out);
944
945
			$return_data = '';
946
			while (!feof($fp))
947
				$return_data .= fgets($fp, 128);
948
949
			fclose($fp);
950
951
			// Get the unique site ID.
952
			preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);
953
954 View Code Duplication
			if (!empty($ID[1]))
0 ignored issues
show
Duplication introduced by
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...
955
				$smcFunc['db_insert']('replace',
956
					$db_prefix . 'settings',
957
					array('variable' => 'string', 'value' => 'string'),
958
					array('allow_sm_stats', $ID[1]),
959
					array('variable')
960
				);
961
		}
962
	}
963
	else
964
		$smcFunc['db_query']('', '
965
			DELETE FROM {db_prefix}settings
966
			WHERE variable = {string:allow_sm_stats}',
967
			array(
968
				'allow_sm_stats' => 'allow_sm_stats',
969
				'db_error_skip' => true,
970
			)
971
		);
972
973
	// Deleting old karma stuff?
974
	if (!empty($_POST['delete_karma']))
975
	{
976
		// Delete old settings vars.
977
		$smcFunc['db_query']('', '
978
			DELETE FROM {db_prefix}settings
979
			WHERE variable IN ({array_string:karma_vars})',
980
			array(
981
				'karma_vars' => array('karmaMode', 'karmaTimeRestrictAdmins', 'karmaWaitTime', 'karmaMinPosts', 'karmaLabel', 'karmaSmiteLabel', 'karmaApplaudLabel'),
982
			)
983
		);
984
985
		// Cleaning up old karma member settings.
986
		if ($upcontext['karma_installed']['good'])
987
			$smcFunc['db_query']('', '
988
				ALTER TABLE {db_prefix}members
989
				DROP karma_good',
990
				array()
991
			);
992
993
		// Does karma bad was enable?
994
		if ($upcontext['karma_installed']['bad'])
995
			$smcFunc['db_query']('', '
996
				ALTER TABLE {db_prefix}members
997
				DROP karma_bad',
998
				array()
999
			);
1000
1001
		// Cleaning up old karma permissions.
1002
		$smcFunc['db_query']('', '
1003
			DELETE FROM {db_prefix}permissions
1004
			WHERE permission = {string:karma_vars}',
1005
			array(
1006
				'karma_vars' => 'karma_edit',
1007
			)
1008
		);
1009
	}
1010
1011
	// Emptying the error log?
1012
	if (!empty($_POST['empty_error']))
1013
		$smcFunc['db_query']('truncate_table', '
1014
			TRUNCATE {db_prefix}log_errors',
1015
			array(
1016
			)
1017
		);
1018
1019
	$changes = array();
1020
1021
	// Add proxy settings.
1022
	if (!isset($GLOBALS['image_proxy_maxsize']))
1023
		$changes += array(
1024
			'image_proxy_secret' => '\'' . substr(sha1(mt_rand()), 0, 20) . '\'',
1025
			'image_proxy_maxsize' => 5190,
1026
			'image_proxy_enabled' => 0,
1027
		);
1028
1029
	// If we're overriding the language follow it through.
1030 View Code Duplication
	if (isset($_GET['lang']) && file_exists($modSettings['theme_dir'] . '/languages/index.' . $_GET['lang'] . '.php'))
0 ignored issues
show
Duplication introduced by
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...
1031
		$changes['language'] = '\'' . $_GET['lang'] . '\'';
1032
1033
	if (!empty($_POST['maint']))
1034
	{
1035
		$changes['maintenance'] = '2';
1036
		// Remember what it was...
1037
		$upcontext['user']['main'] = $maintenance;
1038
1039
		if (!empty($_POST['maintitle']))
1040
		{
1041
			$changes['mtitle'] = '\'' . addslashes($_POST['maintitle']) . '\'';
1042
			$changes['mmessage'] = '\'' . addslashes($_POST['mainmessage']) . '\'';
1043
		}
1044
		else
1045
		{
1046
			$changes['mtitle'] = '\'Upgrading the forum...\'';
1047
			$changes['mmessage'] = '\'Don\\\'t worry, we will be back shortly with an updated forum.  It will only be a minute ;).\'';
1048
		}
1049
	}
1050
1051
	if ($command_line)
1052
		echo ' * Updating Settings.php...';
1053
1054
	// Fix some old paths.
1055 View Code Duplication
	if (substr($boarddir, 0, 1) == '.')
0 ignored issues
show
Duplication introduced by
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...
1056
		$changes['boarddir'] = '\'' . fixRelativePath($boarddir) . '\'';
1057
1058 View Code Duplication
	if (substr($sourcedir, 0, 1) == '.')
0 ignored issues
show
Duplication introduced by
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...
1059
		$changes['sourcedir'] = '\'' . fixRelativePath($sourcedir) . '\'';
1060
1061
	if (empty($cachedir) || substr($cachedir, 0, 1) == '.')
1062
		$changes['cachedir'] = '\'' . fixRelativePath($boarddir) . '/cache\'';
1063
1064
	// Not had the database type added before?
1065
	if (empty($db_type))
1066
		$changes['db_type'] = 'mysql';
1067
1068
	// If they have a "host:port" setup for the host, split that into separate values
1069
	// You should never have a : in the hostname if you're not on MySQL, but better safe than sorry
1070
	if (strpos($db_server, ':') !== false && $db_type == 'mysql')
1071
	{
1072
		list ($db_server, $db_port) = explode(':', $db_server);
1073
1074
		$changes['db_server'] = '\'' . $db_server . '\'';
1075
1076
		// Only set this if we're not using the default port
1077
		if ($db_port != ini_get('mysqli.default_port'))
1078
			$changes['db_port'] = (int) $db_port;
1079
	}
1080
	elseif (!empty($db_port))
0 ignored issues
show
Bug introduced by
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...
1081
	{
1082
		// If db_port is set and is the same as the default, set it to ''
1083
		if ($db_type == 'mysql')
1084
		{
1085
			if ($db_port == ini_get('mysqli.default_port'))
1086
				$changes['db_port'] = '\'\'';
1087
			elseif ($db_type == 'postgresql' && $db_port == 5432)
1088
				$changes['db_port'] = '\'\'';
1089
		}
1090
	}
1091
1092
	// Maybe we haven't had this option yet?
1093
	if (empty($packagesdir))
1094
		$changes['packagesdir'] = '\'' . fixRelativePath($boarddir) . '/Packages\'';
1095
1096
	// Add support for $tasksdir var.
1097
	if (empty($tasksdir))
1098
		$changes['tasksdir'] = '\'' . fixRelativePath($sourcedir) . '/tasks\'';
1099
1100
	// Make sure we fix the language as well.
1101
	if (stristr($language, '-utf8'))
1102
		$changes['language'] = '\'' . str_ireplace('-utf8', '', $language) . '\'';
1103
1104
	// @todo Maybe change the cookie name if going to 1.1, too?
1105
1106
	// Update Settings.php with the new settings.
1107
	require_once($sourcedir . '/Subs-Admin.php');
1108
	updateSettingsFile($changes);
1109
1110
	if ($command_line)
1111
		echo ' Successful.' . "\n";
1112
1113
	// Are we doing debug?
1114
	if (isset($_POST['debug']))
1115
	{
1116
		$upcontext['upgrade_status']['debug'] = true;
1117
		$is_debug = true;
1118
	}
1119
1120
	// If we're not backing up then jump one.
1121
	if (empty($_POST['backup']))
1122
		$upcontext['current_step']++;
1123
1124
	// If we've got here then let's proceed to the next step!
1125
	return true;
1126
}
1127
1128
// Backup the database - why not...
1129
function BackupDatabase()
1130
{
1131
	global $upcontext, $db_prefix, $command_line, $support_js, $file_steps, $smcFunc;
1132
1133
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1134
	$upcontext['page_title'] = 'Backup Database';
1135
1136
	// Done it already - js wise?
1137
	if (!empty($_POST['backup_done']))
1138
		return true;
1139
1140
	// Some useful stuff here.
1141
	db_extend();
1142
1143
	// Might need this as well
1144
	db_extend('packages');
1145
1146
	// Get all the table names.
1147
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1148
	$db = preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? strtr($match[1], array('`' => '')) : false;
1149
	$tables = $smcFunc['db_list_tables']($db, $filter);
1150
1151
	$table_names = array();
1152
	foreach ($tables as $table)
1153
		if (substr($table, 0, 7) !== 'backup_')
1154
			$table_names[] = $table;
1155
1156
	$upcontext['table_count'] = count($table_names);
1157
	$upcontext['cur_table_num'] = $_GET['substep'];
1158
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1159
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1160
	// For non-java auto submit...
1161
	$file_steps = $upcontext['table_count'];
1162
1163
	// What ones have we already done?
1164 View Code Duplication
	foreach ($table_names as $id => $table)
0 ignored issues
show
Duplication introduced by
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...
1165
		if ($id < $_GET['substep'])
1166
			$upcontext['previous_tables'][] = $table;
1167
1168
	if ($command_line)
1169
		echo 'Backing Up Tables.';
1170
1171
	// If we don't support javascript we backup here.
1172
	if (!$support_js || isset($_GET['xml']))
1173
	{
1174
		// Backup each table!
1175
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1176
		{
1177
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1178
			$upcontext['cur_table_num'] = $substep + 1;
1179
1180
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1181
1182
			// Do we need to pause?
1183
			nextSubstep($substep);
1184
1185
			backupTable($table_names[$substep]);
1186
1187
			// If this is XML to keep it nice for the user do one table at a time anyway!
1188
			if (isset($_GET['xml']))
1189
				return upgradeExit();
1190
		}
1191
1192
		if ($command_line)
1193
		{
1194
			echo "\n" . ' Successful.\'' . "\n";
1195
			flush();
1196
		}
1197
		$upcontext['step_progress'] = 100;
1198
1199
		$_GET['substep'] = 0;
1200
		// Make sure we move on!
1201
		return true;
1202
	}
1203
1204
	// Either way next place to post will be database changes!
1205
	$_GET['substep'] = 0;
1206
	return false;
1207
}
1208
1209
// Backup one table...
1210
function backupTable($table)
1211
{
1212
	global $command_line, $db_prefix, $smcFunc;
1213
1214
	if ($command_line)
1215
	{
1216
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1217
		flush();
1218
	}
1219
1220
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1221
1222
	if ($command_line)
1223
		echo ' done.';
1224
}
1225
1226
// Step 2: Everything.
1227
function DatabaseChanges()
1228
{
1229
	global $db_prefix, $modSettings, $smcFunc;
1230
	global $upcontext, $support_js, $db_type;
1231
1232
	// Have we just completed this?
1233
	if (!empty($_POST['database_done']))
1234
		return true;
1235
1236
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1237
	$upcontext['page_title'] = 'Database Changes';
1238
1239
	// All possible files.
1240
	// Name, < version, insert_on_complete
1241
	$files = array(
1242
		array('upgrade_1-0.sql', '1.1', '1.1 RC0'),
1243
		array('upgrade_1-1.sql', '2.0', '2.0 a'),
1244
		array('upgrade_2-0_' . $db_type . '.sql', '2.1', '2.1 dev0'),
1245
		array('upgrade_2-1_' . $db_type . '.sql', '3.0', SMF_VERSION),
1246
	);
1247
1248
	// How many files are there in total?
1249
	if (isset($_GET['filecount']))
1250
		$upcontext['file_count'] = (int) $_GET['filecount'];
1251
	else
1252
	{
1253
		$upcontext['file_count'] = 0;
1254
		foreach ($files as $file)
1255
		{
1256
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1257
				$upcontext['file_count']++;
1258
		}
1259
	}
1260
1261
	// Do each file!
1262
	$did_not_do = count($files) - $upcontext['file_count'];
1263
	$upcontext['step_progress'] = 0;
1264
	$upcontext['cur_file_num'] = 0;
1265
	foreach ($files as $file)
1266
	{
1267
		if ($did_not_do)
1268
			$did_not_do--;
1269
		else
1270
		{
1271
			$upcontext['cur_file_num']++;
1272
			$upcontext['cur_file_name'] = $file[0];
1273
			// Do we actually need to do this still?
1274
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1275
			{
1276
				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1277 View Code Duplication
				if ($nextFile)
0 ignored issues
show
Duplication introduced by
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...
1278
				{
1279
					// Only update the version of this if complete.
1280
					$smcFunc['db_insert']('replace',
1281
						$db_prefix . 'settings',
1282
						array('variable' => 'string', 'value' => 'string'),
1283
						array('smfVersion', $file[2]),
1284
						array('variable')
1285
					);
1286
1287
					$modSettings['smfVersion'] = $file[2];
1288
				}
1289
1290
				// If this is XML we only do this stuff once.
1291
				if (isset($_GET['xml']))
1292
				{
1293
					// Flag to move on to the next.
1294
					$upcontext['completed_step'] = true;
1295
					// Did we complete the whole file?
1296
					if ($nextFile)
1297
						$upcontext['current_debug_item_num'] = -1;
1298
					return upgradeExit();
1299
				}
1300
				elseif ($support_js)
1301
					break;
1302
			}
1303
			// Set the progress bar to be right as if we had - even if we hadn't...
1304
			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1305
		}
1306
	}
1307
1308
	$_GET['substep'] = 0;
1309
	// So the template knows we're done.
1310
	if (!$support_js)
1311
	{
1312
		$upcontext['changes_complete'] = true;
1313
1314
		return true;
1315
	}
1316
	return false;
1317
}
1318
1319
1320
// Delete the damn thing!
1321
function DeleteUpgrade()
1322
{
1323
	global $command_line, $language, $upcontext, $boarddir, $sourcedir, $forum_version, $user_info, $maintenance, $smcFunc, $db_type;
1324
1325
	// Now it's nice to have some of the basic SMF source files.
1326
	if (!isset($_GET['ssi']) && !$command_line)
1327
		redirectLocation('&ssi=1');
1328
1329
	$upcontext['sub_template'] = 'upgrade_complete';
1330
	$upcontext['page_title'] = 'Upgrade Complete';
1331
1332
	$endl = $command_line ? "\n" : '<br>' . "\n";
1333
1334
	$changes = array(
1335
		'language' => '\'' . (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language) . '\'',
1336
		'db_error_send' => '1',
1337
		'upgradeData' => '\'\'',
1338
	);
1339
1340
	// Are we in maintenance mode?
1341
	if (isset($upcontext['user']['main']))
1342
	{
1343
		if ($command_line)
1344
			echo ' * ';
1345
		$upcontext['removed_maintenance'] = true;
1346
		$changes['maintenance'] = $upcontext['user']['main'];
1347
	}
1348
	// Otherwise if somehow we are in 2 let's go to 1.
1349
	elseif (!empty($maintenance) && $maintenance == 2)
1350
		$changes['maintenance'] = 1;
1351
1352
	// Wipe this out...
1353
	$upcontext['user'] = array();
1354
1355
	require_once($sourcedir . '/Subs-Admin.php');
1356
	updateSettingsFile($changes);
1357
1358
	// Clean any old cache files away.
1359
	upgrade_clean_cache();
1360
1361
	// Can we delete the file?
1362
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1363
1364
	// Now is the perfect time to fetch the SM files.
1365
	if ($command_line)
1366
		cli_scheduled_fetchSMfiles();
1367
	else
1368
	{
1369
		require_once($sourcedir . '/ScheduledTasks.php');
1370
		$forum_version = SMF_VERSION; // The variable is usually defined in index.php so lets just use the constant to do it for us.
1371
		scheduled_fetchSMfiles(); // Now go get those files!
1372
	}
1373
1374
	// Log what we've done.
1375
	if (empty($user_info['id']))
1376
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
1377
1378
	// Log the action manually, so CLI still works.
1379
	$smcFunc['db_insert']('',
1380
		'{db_prefix}log_actions',
1381
		array(
1382
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'inet', 'action' => 'string',
1383
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
1384
		),
1385
		array(
1386
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
1387
			0, 0, 0, json_encode(array('version' => $forum_version, 'member' => $user_info['id'])),
1388
		),
1389
		array('id_action')
1390
	);
1391
	$user_info['id'] = 0;
1392
1393
	// Save the current database version.
1394
	$server_version = $smcFunc['db_server_info']();
1395 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
Duplication introduced by
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...
1396
		updateSettings(array('db_mysql_group_by_fix' => '1'));
1397
1398
	if ($command_line)
1399
	{
1400
		echo $endl;
1401
		echo 'Upgrade Complete!', $endl;
1402
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
1403
		exit;
1404
	}
1405
1406
	// Make sure it says we're done.
1407
	$upcontext['overall_percent'] = 100;
1408
	if (isset($upcontext['step_progress']))
1409
		unset($upcontext['step_progress']);
1410
1411
	$_GET['substep'] = 0;
1412
	return false;
1413
}
1414
1415
// Just like the built in one, but setup for CLI to not use themes.
1416
function cli_scheduled_fetchSMfiles()
1417
{
1418
	global $sourcedir, $language, $forum_version, $modSettings, $smcFunc;
1419
1420
	if (empty($modSettings['time_format']))
1421
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1422
1423
	// What files do we want to get
1424
	$request = $smcFunc['db_query']('', '
1425
		SELECT id_file, filename, path, parameters
1426
		FROM {db_prefix}admin_info_files',
1427
		array(
1428
		)
1429
	);
1430
1431
	$js_files = array();
1432 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Duplication introduced by
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...
1433
	{
1434
		$js_files[$row['id_file']] = array(
1435
			'filename' => $row['filename'],
1436
			'path' => $row['path'],
1437
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode($forum_version)),
1438
		);
1439
	}
1440
	$smcFunc['db_free_result']($request);
1441
1442
	// We're gonna need fetch_web_data() to pull this off.
1443
	require_once($sourcedir . '/Subs-Package.php');
1444
1445
	foreach ($js_files as $ID_FILE => $file)
1446
	{
1447
		// Create the url
1448
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'https://www.simplemachines.org' : '';
1449
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1450
1451
		// Get the file
1452
		$file_data = fetch_web_data($url);
1453
1454
		// If we got an error - give up - the site might be down.
1455
		if ($file_data === false)
1456
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
1457
1458
		// Save the file to the database.
1459
		$smcFunc['db_query']('substring', '
1460
			UPDATE {db_prefix}admin_info_files
1461
			SET data = SUBSTRING({string:file_data}, 1, 65534)
1462
			WHERE id_file = {int:id_file}',
1463
			array(
1464
				'id_file' => $ID_FILE,
1465
				'file_data' => $file_data,
1466
			)
1467
		);
1468
	}
1469
	return true;
1470
}
1471
1472
function convertSettingsToTheme()
1473
{
1474
	global $db_prefix, $modSettings, $smcFunc;
1475
1476
	$values = array(
1477
		'show_latest_member' => @$GLOBALS['showlatestmember'],
1478
		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
1479
		'show_modify' => @$GLOBALS['showmodify'],
1480
		'show_user_images' => @$GLOBALS['showuserpic'],
1481
		'show_blurb' => @$GLOBALS['showusertext'],
1482
		'show_gender' => @$GLOBALS['showgenderimage'],
1483
		'show_newsfader' => @$GLOBALS['shownewsfader'],
1484
		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
1485
		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
1486
		'linktree_link' => @$GLOBALS['curposlinks'],
1487
		'show_profile_buttons' => @$GLOBALS['profilebutton'],
1488
		'show_mark_read' => @$GLOBALS['showmarkread'],
1489
		'newsfader_time' => @$GLOBALS['fadertime'],
1490
		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
1491
		'enable_news' => @$GLOBALS['enable_news'],
1492
		'return_to_post' => @$modSettings['returnToPost'],
1493
	);
1494
1495
	$themeData = array();
1496
	foreach ($values as $variable => $value)
1497
	{
1498
		if (!isset($value) || $value === null)
1499
			$value = 0;
1500
1501
		$themeData[] = array(0, 1, $variable, $value);
1502
	}
1503 View Code Duplication
	if (!empty($themeData))
0 ignored issues
show
Duplication introduced by
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...
1504
	{
1505
		$smcFunc['db_insert']('ignore',
1506
			$db_prefix . 'themes',
1507
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
1508
			$themeData,
1509
			array('id_member', 'id_theme', 'variable')
1510
		);
1511
	}
1512
}
1513
1514
// This function only works with MySQL but that's fine as it is only used for v1.0.
1515
function convertSettingstoOptions()
1516
{
1517
	global $modSettings, $smcFunc;
1518
1519
	// Format: new_setting -> old_setting_name.
1520
	$values = array(
1521
		'calendar_start_day' => 'cal_startmonday',
1522
		'view_newest_first' => 'viewNewestFirst',
1523
		'view_newest_pm_first' => 'viewNewestFirst',
1524
	);
1525
1526
	foreach ($values as $variable => $value)
1527
	{
1528
		if (empty($modSettings[$value[0]]))
1529
			continue;
1530
1531
		$smcFunc['db_query']('', '
1532
			INSERT IGNORE INTO {db_prefix}themes
1533
				(id_member, id_theme, variable, value)
1534
			SELECT id_member, 1, {string:variable}, {string:value}
1535
			FROM {db_prefix}members',
1536
			array(
1537
				'variable' => $variable,
1538
				'value' => $modSettings[$value[0]],
1539
				'db_error_skip' => true,
1540
			)
1541
		);
1542
1543
		$smcFunc['db_query']('', '
1544
			INSERT IGNORE INTO {db_prefix}themes
1545
				(id_member, id_theme, variable, value)
1546
			VALUES (-1, 1, {string:variable}, {string:value})',
1547
			array(
1548
				'variable' => $variable,
1549
				'value' => $modSettings[$value[0]],
1550
				'db_error_skip' => true,
1551
			)
1552
		);
1553
	}
1554
}
1555
1556
function php_version_check()
1557
{
1558
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
1559
}
1560
1561
function db_version_check()
1562
{
1563
	global $db_type, $databases;
1564
1565
	$curver = eval($databases[$db_type]['version_check']);
0 ignored issues
show
Coding Style introduced by
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...
1566
	$curver = preg_replace('~\-.+?$~', '', $curver);
1567
1568
	return version_compare($databases[$db_type]['version'], $curver, '<=');
1569
}
1570
1571
function fixRelativePath($path)
1572
{
1573
	global $install_path;
1574
1575
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
1576
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
1577
}
1578
1579
function parse_sql($filename)
1580
{
1581
	global $db_prefix, $db_collation, $boarddir, $boardurl, $command_line, $file_steps, $step_progress, $custom_warning;
1582
	global $upcontext, $support_js, $is_debug, $smcFunc, $databases, $db_type, $db_character_set;
1583
1584
/*
1585
	Failure allowed on:
1586
		- INSERT INTO but not INSERT IGNORE INTO.
1587
		- UPDATE IGNORE but not UPDATE.
1588
		- ALTER TABLE and ALTER IGNORE TABLE.
1589
		- DROP TABLE.
1590
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
1591
1592
	If a comment...
1593
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
1594
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
1595
		- is only ---#, it is "done." and then a break - only shown in debug.
1596
		- begins with ---{ it is a code block terminating at ---}.
1597
1598
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
1599
1600
	Replaces the following variables:
1601
		- {$boarddir}
1602
		- {$boardurl}
1603
		- {$db_prefix}
1604
		- {$db_collation}
1605
*/
1606
1607
	// May want to use extended functionality.
1608
	db_extend();
1609
	db_extend('packages');
1610
1611
	// Our custom error handler - does nothing but does stop public errors from XML!
1612
	set_error_handler(
1613
		function ($errno, $errstr, $errfile, $errline) use ($support_js)
1614
		{
1615
			if ($support_js)
1616
				return true;
1617
			else
1618
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
1619
		}
1620
	);
1621
1622
	// If we're on MySQL, set {db_collation}; this approach is used throughout upgrade_2-0_mysql.php to set new tables to utf8
1623
	// Note it is expected to be in the format: ENGINE=MyISAM{$db_collation};
1624
	if ($db_type == 'mysql')
1625
		$db_collation = ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1626
	else
1627
		$db_collation = '';
1628
1629
	$endl = $command_line ? "\n" : '<br>' . "\n";
1630
1631
	$lines = file($filename);
1632
1633
	$current_type = 'sql';
1634
	$current_data = '';
1635
	$substep = 0;
1636
	$last_step = '';
1637
1638
	// Make sure all newly created tables will have the proper characters set; this approach is used throughout upgrade_2-1_mysql.php
1639
	if (isset($db_character_set) && $db_character_set === 'utf8')
1640
		$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
1641
1642
	// Count the total number of steps within this file - for progress.
1643
	$file_steps = substr_count(implode('', $lines), '---#');
1644
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
1645
	$upcontext['debug_items'] = $file_steps;
1646
	$upcontext['current_item_num'] = 0;
1647
	$upcontext['current_item_name'] = '';
1648
	$upcontext['current_debug_item_num'] = 0;
1649
	$upcontext['current_debug_item_name'] = '';
1650
	// This array keeps a record of what we've done in case java is dead...
1651
	$upcontext['actioned_items'] = array();
1652
1653
	$done_something = false;
1654
1655
	foreach ($lines as $line_number => $line)
1656
	{
1657
		$do_current = $substep >= $_GET['substep'];
1658
1659
		// Get rid of any comments in the beginning of the line...
1660
		if (substr(trim($line), 0, 2) === '/*')
1661
			$line = preg_replace('~/\*.+?\*/~', '', $line);
1662
1663
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
1664
		if ($is_debug && !$support_js && $command_line)
1665
			flush();
1666
1667
		if (trim($line) === '')
1668
			continue;
1669
1670
		if (trim(substr($line, 0, 3)) === '---')
1671
		{
1672
			$type = substr($line, 3, 1);
1673
1674
			// An error??
1675
			if (trim($current_data) != '' && $type !== '}')
1676
			{
1677
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
1678
				if ($command_line)
1679
					echo $upcontext['error_message'];
1680
			}
1681
1682
			if ($type == ' ')
1683
			{
1684
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
1685
				{
1686
					echo ' Successful.', $endl;
1687
					flush();
1688
				}
1689
1690
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
1691
				$upcontext['current_item_num']++;
1692
				$upcontext['current_item_name'] = $last_step;
1693
1694
				if ($do_current)
1695
				{
1696
					$upcontext['actioned_items'][] = $last_step;
1697
					if ($command_line)
1698
						echo ' * ';
1699
				}
1700
			}
1701
			elseif ($type == '#')
1702
			{
1703
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
1704
1705
				$upcontext['current_debug_item_num']++;
1706
				if (trim($line) != '---#')
1707
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
1708
1709
				// Have we already done something?
1710
				if (isset($_GET['xml']) && $done_something)
1711
				{
1712
					restore_error_handler();
1713
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
1714
				}
1715
1716
				if ($do_current)
1717
				{
1718
					if (trim($line) == '---#' && $command_line)
1719
						echo ' done.', $endl;
1720
					elseif ($command_line)
1721
						echo ' +++ ', rtrim(substr($line, 4));
1722
					elseif (trim($line) != '---#')
1723
					{
1724
						if ($is_debug)
1725
							$upcontext['actioned_items'][] = htmlspecialchars(rtrim(substr($line, 4)));
1726
					}
1727
				}
1728
1729
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
1730
				{
1731
					if ($command_line)
1732
						echo ' * ';
1733
					else
1734
						$upcontext['actioned_items'][] = $last_step;
1735
				}
1736
1737
				// Small step - only if we're actually doing stuff.
1738
				if ($do_current)
1739
					nextSubstep(++$substep);
1740
				else
1741
					$substep++;
1742
			}
1743
			elseif ($type == '{')
1744
				$current_type = 'code';
1745
			elseif ($type == '}')
1746
			{
1747
				$current_type = 'sql';
1748
1749
				if (!$do_current)
1750
				{
1751
					$current_data = '';
1752
					continue;
1753
				}
1754
1755
				if (eval('global $db_prefix, $modSettings, $smcFunc; ' . $current_data) === false)
0 ignored issues
show
Coding Style introduced by
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...
1756
				{
1757
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
1758
					if ($command_line)
1759
						echo $upcontext['error_message'];
1760
				}
1761
1762
				// Done with code!
1763
				$current_data = '';
1764
				$done_something = true;
1765
			}
1766
1767
			continue;
1768
		}
1769
1770
		$current_data .= $line;
1771
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
1772
		{
1773
			if ((!$support_js || isset($_GET['xml'])))
1774
			{
1775
				if (!$do_current)
1776
				{
1777
					$current_data = '';
1778
					continue;
1779
				}
1780
1781
				$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));
1782
1783
				upgrade_query($current_data);
1784
1785
				// @todo This will be how it kinda does it once mysql all stripped out - needed for postgre (etc).
1786
				/*
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...
1787
				$result = $smcFunc['db_query']('', $current_data, false, false);
1788
				// Went wrong?
1789
				if (!$result)
1790
				{
1791
					// Bit of a bodge - do we want the error?
1792
					if (!empty($upcontext['return_error']))
1793
					{
1794
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
1795
						return false;
1796
					}
1797
				}*/
1798
				$done_something = true;
1799
			}
1800
			$current_data = '';
1801
		}
1802
		// If this is xml based and we're just getting the item name then that's grand.
1803
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
1804
		{
1805
			restore_error_handler();
1806
			return false;
1807
		}
1808
1809
		// Clean up by cleaning any step info.
1810
		$step_progress = array();
1811
		$custom_warning = '';
1812
	}
1813
1814
	// Put back the error handler.
1815
	restore_error_handler();
1816
1817
	if ($command_line)
1818
	{
1819
		echo ' Successful.' . "\n";
1820
		flush();
1821
	}
1822
1823
	$_GET['substep'] = 0;
1824
	return true;
1825
}
1826
1827
function upgrade_query($string, $unbuffered = false)
1828
{
1829
	global $db_connection, $db_server, $db_user, $db_passwd, $db_type, $command_line, $upcontext, $upgradeurl, $modSettings;
1830
	global $db_name, $db_unbuffered, $smcFunc;
1831
1832
	// Get the query result - working around some SMF specific security - just this once!
1833
	$modSettings['disableQueryCheck'] = true;
1834
	$db_unbuffered = $unbuffered;
1835
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
1836
	$db_unbuffered = false;
1837
1838
	// Failure?!
1839
	if ($result !== false)
1840
		return $result;
1841
1842
	$db_error_message = $smcFunc['db_error']($db_connection);
1843
	// If MySQL we do something more clever.
1844
	if ($db_type == 'mysql')
1845
	{
1846
		$mysqli_errno = mysqli_errno($db_connection);
1847
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR'));
1848
1849
		// Error numbers:
1850
		//    1016: Can't open file '....MYI'
1851
		//    1050: Table already exists.
1852
		//    1054: Unknown column name.
1853
		//    1060: Duplicate column name.
1854
		//    1061: Duplicate key name.
1855
		//    1062: Duplicate entry for unique key.
1856
		//    1068: Multiple primary keys.
1857
		//    1072: Key column '%s' doesn't exist in table.
1858
		//    1091: Can't drop key, doesn't exist.
1859
		//    1146: Table doesn't exist.
1860
		//    2013: Lost connection to server during query.
1861
1862
		if ($mysqli_errno == 1016)
1863
		{
1864
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
1865
			{
1866
				mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
1867
				$result = mysqli_query($db_connection, $string);
1868
				if ($result !== false)
1869
					return $result;
1870
			}
1871
		}
1872
		elseif ($mysqli_errno == 2013)
1873
		{
1874
			$db_connection = mysqli_connect($db_server, $db_user, $db_passwd);
1875
			mysqli_select_db($db_connection, $db_name);
1876
			if ($db_connection)
1877
			{
1878
				$result = mysqli_query($db_connection, $string);
1879
				if ($result !== false)
1880
					return $result;
1881
			}
1882
		}
1883
		// Duplicate column name... should be okay ;).
1884 View Code Duplication
		elseif (in_array($mysqli_errno, array(1060, 1061, 1068, 1091)))
0 ignored issues
show
Duplication introduced by
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...
1885
			return false;
1886
		// Duplicate insert... make sure it's the proper type of query ;).
1887 View Code Duplication
		elseif (in_array($mysqli_errno, array(1054, 1062, 1146)) && $error_query)
0 ignored issues
show
Duplication introduced by
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...
1888
			return false;
1889
		// Creating an index on a non-existent column.
1890
		elseif ($mysqli_errno == 1072)
1891
			return false;
1892
		elseif ($mysqli_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
1893
			return false;
1894
	}
1895
	// If a table already exists don't go potty.
1896
	else
1897
	{
1898
		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U')))
1899
		{
1900
			if (strpos($db_error_message, 'exist') !== false)
1901
				return true;
1902
		}
1903
		elseif (strpos(trim($string), 'INSERT ') !== false)
1904
		{
1905
			if (strpos($db_error_message, 'duplicate') !== false)
1906
				return true;
1907
		}
1908
	}
1909
1910
	// Get the query string so we pass everything.
1911
	$query_string = '';
1912
	foreach ($_GET as $k => $v)
1913
		$query_string .= ';' . $k . '=' . $v;
1914
	if (strlen($query_string) != 0)
1915
		$query_string = '?' . substr($query_string, 1);
1916
1917
	if ($command_line)
1918
	{
1919
		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
1920
		die;
1921
	}
1922
1923
	// Bit of a bodge - do we want the error?
1924
	if (!empty($upcontext['return_error']))
1925
	{
1926
		$upcontext['error_message'] = $db_error_message;
1927
		$upcontext['error_string'] = $string;
1928
		return false;
1929
	}
1930
1931
	// Otherwise we have to display this somewhere appropriate if possible.
1932
	$upcontext['forced_error_message'] = '
1933
			<strong>Unsuccessful!</strong><br>
1934
1935
			<div style="margin: 2ex;">
1936
				This query:
1937
				<blockquote><pre>' . nl2br(htmlspecialchars(trim($string))) . ';</pre></blockquote>
1938
1939
				Caused the error:
1940
				<blockquote>' . nl2br(htmlspecialchars($db_error_message)) . '</blockquote>
1941
			</div>
1942
1943
			<form action="' . $upgradeurl . $query_string . '" method="post">
1944
				<input type="submit" value="Try again" class="button_submit">
1945
			</form>
1946
		</div>';
1947
1948
	upgradeExit();
1949
}
1950
1951
// This performs a table alter, but does it unbuffered so the script can time out professionally.
1952
function protected_alter($change, $substep, $is_test = false)
1953
{
1954
	global $db_prefix, $smcFunc;
1955
1956
	db_extend('packages');
1957
1958
	// Firstly, check whether the current index/column exists.
1959
	$found = false;
1960
	if ($change['type'] === 'column')
1961
	{
1962
		$columns = $smcFunc['db_list_columns']('{db_prefix}' . $change['table'], true);
1963
		foreach ($columns as $column)
1964
		{
1965
			// Found it?
1966
			if ($column['name'] === $change['name'])
1967
			{
1968
				$found |= 1;
1969
				// Do some checks on the data if we have it set.
1970
				if (isset($change['col_type']))
1971
					$found &= $change['col_type'] === $column['type'];
1972
				if (isset($change['null_allowed']))
1973
					$found &= $column['null'] == $change['null_allowed'];
1974
				if (isset($change['default']))
1975
					$found &= $change['default'] === $column['default'];
1976
			}
1977
		}
1978
	}
1979
	elseif ($change['type'] === 'index')
1980
	{
1981
		$request = upgrade_query('
1982
			SHOW INDEX
1983
			FROM ' . $db_prefix . $change['table']);
1984
		if ($request !== false)
1985
		{
1986
			$cur_index = array();
1987
1988
			while ($row = $smcFunc['db_fetch_assoc']($request))
1989
				if ($row['Key_name'] === $change['name'])
1990
					$cur_index[(int) $row['Seq_in_index']] = $row['Column_name'];
1991
1992
			ksort($cur_index, SORT_NUMERIC);
1993
			$found = array_values($cur_index) === $change['target_columns'];
1994
1995
			$smcFunc['db_free_result']($request);
1996
		}
1997
	}
1998
1999
	// If we're trying to add and it's added, we're done.
2000
	if ($found && in_array($change['method'], array('add', 'change')))
2001
		return true;
2002
	// Otherwise if we're removing and it wasn't found we're also done.
2003
	elseif (!$found && in_array($change['method'], array('remove', 'change_remove')))
2004
		return true;
2005
	// Otherwise is it just a test?
2006
	elseif ($is_test)
2007
		return false;
2008
2009
	// Not found it yet? Bummer! How about we see if we're currently doing it?
2010
	$running = false;
2011
	$found = false;
2012
	while (1 == 1)
2013
	{
2014
		$request = upgrade_query('
2015
			SHOW FULL PROCESSLIST');
2016
		while ($row = $smcFunc['db_fetch_assoc']($request))
2017
		{
2018
			if (strpos($row['Info'], 'ALTER TABLE ' . $db_prefix . $change['table']) !== false && strpos($row['Info'], $change['text']) !== false)
2019
				$found = true;
2020
		}
2021
2022
		// Can't find it? Then we need to run it fools!
2023
		if (!$found && !$running)
2024
		{
2025
			$smcFunc['db_free_result']($request);
2026
2027
			$success = upgrade_query('
2028
				ALTER TABLE ' . $db_prefix . $change['table'] . '
2029
				' . $change['text'], true) !== false;
2030
2031
			if (!$success)
2032
				return false;
2033
2034
			// Return
2035
			$running = true;
2036
		}
2037
		// What if we've not found it, but we'd ran it already? Must of completed.
2038
		elseif (!$found)
2039
		{
2040
			$smcFunc['db_free_result']($request);
2041
			return true;
2042
		}
2043
2044
		// Pause execution for a sec or three.
2045
		sleep(3);
2046
2047
		// Can never be too well protected.
2048
		nextSubstep($substep);
2049
	}
2050
2051
	// Protect it.
2052
	nextSubstep($substep);
2053
}
2054
2055
/**
2056
 * Alter a text column definition preserving its character set.
2057
 *
2058
 * @param array $change
2059
 * @param int $substep
2060
 */
2061
function textfield_alter($change, $substep)
2062
{
2063
	global $db_prefix, $smcFunc;
2064
2065
	$request = $smcFunc['db_query']('', '
2066
		SHOW FULL COLUMNS
2067
		FROM {db_prefix}' . $change['table'] . '
2068
		LIKE {string:column}',
2069
		array(
2070
			'column' => $change['column'],
2071
			'db_error_skip' => true,
2072
		)
2073
	);
2074
	if ($smcFunc['db_num_rows']($request) === 0)
2075
		die('Unable to find column ' . $change['column'] . ' inside table ' . $db_prefix . $change['table']);
2076
	$table_row = $smcFunc['db_fetch_assoc']($request);
2077
	$smcFunc['db_free_result']($request);
2078
2079
	// If something of the current column definition is different, fix it.
2080
	$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']);
2081
2082
	// Columns that previously allowed null, need to be converted first.
2083
	$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
2084
2085
	// Get the character set that goes with the collation of the column.
2086 View Code Duplication
	if ($column_fix && !empty($table_row['Collation']))
0 ignored issues
show
Duplication introduced by
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...
2087
	{
2088
		$request = $smcFunc['db_query']('', '
2089
			SHOW COLLATION
2090
			LIKE {string:collation}',
2091
			array(
2092
				'collation' => $table_row['Collation'],
2093
				'db_error_skip' => true,
2094
			)
2095
		);
2096
		// No results? Just forget it all together.
2097
		if ($smcFunc['db_num_rows']($request) === 0)
2098
			unset($table_row['Collation']);
2099
		else
2100
			$collation_info = $smcFunc['db_fetch_assoc']($request);
2101
		$smcFunc['db_free_result']($request);
2102
	}
2103
2104
	if ($column_fix)
2105
	{
2106
		// Make sure there are no NULL's left.
2107
		if ($null_fix)
2108
			$smcFunc['db_query']('', '
2109
				UPDATE {db_prefix}' . $change['table'] . '
2110
				SET ' . $change['column'] . ' = {string:default}
2111
				WHERE ' . $change['column'] . ' IS NULL',
2112
				array(
2113
					'default' => isset($change['default']) ? $change['default'] : '',
2114
					'db_error_skip' => true,
2115
				)
2116
			);
2117
2118
		// Do the actual alteration.
2119
		$smcFunc['db_query']('', '
2120
			ALTER TABLE {db_prefix}' . $change['table'] . '
2121
			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}' : ''),
2122
			array(
2123
				'default' => isset($change['default']) ? $change['default'] : '',
2124
				'db_error_skip' => true,
2125
			)
2126
		);
2127
	}
2128
	nextSubstep($substep);
2129
}
2130
2131
// Check if we need to alter this query.
2132
function checkChange(&$change)
2133
{
2134
	global $smcFunc, $db_type, $databases;
2135
	static $database_version, $where_field_support;
2136
2137
	// Attempt to find a database_version.
2138
	if (empty($database_version))
2139
	{
2140
		$database_version = $databases[$db_type]['version_check'];
2141
		$where_field_support = $db_type == 'mysql' && version_compare('5.0', $database_version, '<=');
2142
	}
2143
2144
	// Not a column we need to check on?
2145
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
2146
		return;
2147
2148
	// Break it up you (six|seven).
2149
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
2150
2151
	// Can we support a shortcut method?
2152
	if ($where_field_support)
2153
	{
2154
		// Get the details about this change.
2155
		$request = $smcFunc['db_query']('', '
2156
			SHOW FIELDS
2157
			FROM {db_prefix}{raw:table}
2158
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
2159
			array(
2160
				'table' => $change['table'],
2161
				'old_name' => $temp[1],
2162
				'new_name' => $temp[2],
2163
		));
2164
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2165
		if ($smcFunc['db_num_rows'] != 1)
2166
			return;
2167
2168
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
2169
		$smcFunc['db_free_result']($request);
2170
	}
2171
	else
2172
	{
2173
		// Do this the old fashion, sure method way.
2174
		$request = $smcFunc['db_query']('', '
2175
			SHOW FIELDS
2176
			FROM {db_prefix}{raw:table}',
2177
			array(
2178
				'table' => $change['table'],
2179
		));
2180
		// Mayday!
2181
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2182
		if ($smcFunc['db_num_rows'] == 0)
2183
			return;
2184
2185
		// Oh where, oh where has my little field gone. Oh where can it be...
2186
		while ($row = $smcFunc['db_query']($request))
2187
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
2188
			{
2189
				$current_type = $row['Type'];
2190
				break;
2191
			}
2192
	}
2193
2194
	// If this doesn't match, the column may of been altered for a reason.
2195
	if (trim($current_type) != trim($temp[3]))
2196
		$temp[3] = $current_type;
0 ignored issues
show
Bug introduced by
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...
2197
2198
	// Piece this back together.
2199
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
2200
}
2201
2202
// The next substep.
2203
function nextSubstep($substep)
2204
{
2205
	global $start_time, $timeLimitThreshold, $command_line, $custom_warning;
2206
	global $step_progress, $is_debug, $upcontext;
2207
2208
	if ($_GET['substep'] < $substep)
2209
		$_GET['substep'] = $substep;
2210
2211
	if ($command_line)
2212
	{
2213
		if (time() - $start_time > 1 && empty($is_debug))
2214
		{
2215
			echo '.';
2216
			$start_time = time();
2217
		}
2218
		return;
2219
	}
2220
2221
	@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...
2222
	if (function_exists('apache_reset_timeout'))
2223
		@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...
2224
2225
	if (time() - $start_time <= $timeLimitThreshold)
2226
		return;
2227
2228
	// Do we have some custom step progress stuff?
2229
	if (!empty($step_progress))
2230
	{
2231
		$upcontext['substep_progress'] = 0;
2232
		$upcontext['substep_progress_name'] = $step_progress['name'];
2233
		if ($step_progress['current'] > $step_progress['total'])
2234
			$upcontext['substep_progress'] = 99.9;
2235
		else
2236
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2237
2238
		// Make it nicely rounded.
2239
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2240
	}
2241
2242
	// If this is XML we just exit right away!
2243
	if (isset($_GET['xml']))
2244
		return upgradeExit();
2245
2246
	// We're going to pause after this!
2247
	$upcontext['pause'] = true;
2248
2249
	$upcontext['query_string'] = '';
2250
	foreach ($_GET as $k => $v)
2251
	{
2252
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2253
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2254
	}
2255
2256
	// Custom warning?
2257
	if (!empty($custom_warning))
2258
		$upcontext['custom_warning'] = $custom_warning;
2259
2260
	upgradeExit();
2261
}
2262
2263
function cmdStep0()
2264
{
2265
	global $boarddir, $sourcedir, $modSettings, $start_time, $cachedir, $databases, $db_type, $smcFunc, $upcontext;
2266
	global $is_debug;
2267
	$start_time = time();
2268
2269
	ob_end_clean();
2270
	ob_implicit_flush(true);
2271
	@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...
2272
2273
	if (!isset($_SERVER['argv']))
2274
		$_SERVER['argv'] = array();
2275
	$_GET['maint'] = 1;
2276
2277
	foreach ($_SERVER['argv'] as $i => $arg)
2278
	{
2279
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2280
			$_GET['lang'] = $match[1];
2281
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2282
			continue;
2283
		elseif ($arg == '--no-maintenance')
2284
			$_GET['maint'] = 0;
2285
		elseif ($arg == '--debug')
2286
			$is_debug = true;
2287
		elseif ($arg == '--backup')
2288
			$_POST['backup'] = 1;
2289
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2290
			$_GET['conv'] = 1;
2291
		elseif ($i != 0)
2292
		{
2293
			echo 'SMF Command-line Upgrader
2294
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2295
2296
    --language=LANG         Reset the forum\'s language to LANG.
2297
    --no-maintenance        Don\'t put the forum into maintenance mode.
2298
    --debug                 Output debugging information.
2299
    --backup                Create backups of tables with "backup_" prefix.';
2300
			echo "\n";
2301
			exit;
2302
		}
2303
	}
2304
2305
	if (!php_version_check())
2306
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
2307
	if (!db_version_check())
2308
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2309
2310
	// Do some checks to make sure they have proper privileges
2311
	db_extend('packages');
2312
2313
	// CREATE
2314
	$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');
2315
2316
	// ALTER
2317
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'tinytext', 'null' => false, 'default' => ''));
2318
2319
	// DROP
2320
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2321
2322
	// Sorry... we need CREATE, ALTER and DROP
2323 View Code Duplication
	if (!$create || !$alter || !$drop)
0 ignored issues
show
Duplication introduced by
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...
2324
		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);
2325
2326
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2327
		&& @file_exists($sourcedir . '/QueryString.php')
2328
		&& @file_exists($sourcedir . '/ManageBoards.php');
2329
	if (!$check && !isset($modSettings['smfVersion']))
2330
		print_error('Error: Some files are missing or out-of-date.', true);
2331
2332
	// Do a quick version spot check.
2333
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
2334
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2335
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2336
		print_error('Error: Some files have not yet been updated properly.');
2337
2338
	// Make sure Settings.php is writable.
2339
		quickFileWritable($boarddir . '/Settings.php');
2340
	if (!is_writable($boarddir . '/Settings.php'))
2341
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2342
2343
	// Make sure Settings_bak.php is writable.
2344
		quickFileWritable($boarddir . '/Settings_bak.php');
2345
	if (!is_writable($boarddir . '/Settings_bak.php'))
2346
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2347
2348 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
0 ignored issues
show
Duplication introduced by
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...
2349
		print_error('Error: Unable to obtain write access to "agreement.txt".');
2350
	elseif (isset($modSettings['agreement']))
2351
	{
2352
		$fp = fopen($boarddir . '/agreement.txt', 'w');
2353
		fwrite($fp, $modSettings['agreement']);
2354
		fclose($fp);
2355
	}
2356
2357
	// Make sure Themes is writable.
2358
	quickFileWritable($modSettings['theme_dir']);
2359
2360
	if (!is_writable($modSettings['theme_dir']) && !isset($modSettings['smfVersion']))
2361
		print_error('Error: Unable to obtain write access to "Themes".');
2362
2363
	// Make sure cache directory exists and is writable!
2364
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
2365
	if (!file_exists($cachedir_temp))
2366
		@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...
2367
2368
	// Make sure the cache temp dir is writable.
2369
	quickFileWritable($cachedir_temp);
2370
2371
	if (!is_writable($cachedir_temp))
2372
		print_error('Error: Unable to obtain write access to "cache".', true);
2373
2374
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
2375
		print_error('Error: Unable to find language files!', true);
2376
	else
2377
	{
2378
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
2379
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
2380
2381
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
2382
			print_error('Error: Language files out of date.', true);
2383
		if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
2384
			print_error('Error: Install language is missing for selected language.', true);
2385
2386
		// Otherwise include it!
2387
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
2388
	}
2389
2390
	// Make sure we skip the HTML for login.
2391
	$_POST['upcont'] = true;
2392
	$upcontext['current_step'] = 1;
2393
}
2394
2395
/**
2396
 * Handles converting your database to UTF-8
2397
 */
2398
function ConvertUtf8()
2399
{
2400
	global $upcontext, $db_character_set, $sourcedir, $smcFunc, $modSettings, $language, $db_prefix, $db_type, $command_line, $support_js;
2401
2402
	// Done it already?
2403
	if (!empty($_POST['utf8_done']))
2404
		return true;
2405
2406
	// First make sure they aren't already on UTF-8 before we go anywhere...
2407
	if ($db_type == 'postgresql' || ($db_character_set === 'utf8' && !empty($modSettings['global_character_set']) && $modSettings['global_character_set'] === 'UTF-8'))
2408
	{
2409
		$smcFunc['db_insert']('replace',
2410
			'{db_prefix}settings',
2411
			array('variable' => 'string', 'value' => 'string'),
2412
			array(array('global_character_set', 'UTF-8')),
2413
			array('variable')
2414
		);
2415
2416
		return true;
2417
	}
2418
	else
2419
	{
2420
		$upcontext['page_title'] = 'Converting to UTF8';
2421
		$upcontext['sub_template'] = isset($_GET['xml']) ? 'convert_xml' : 'convert_utf8';
2422
2423
		// The character sets used in SMF's language files with their db equivalent.
2424
		$charsets = array(
2425
			// Armenian
2426
			'armscii8' => 'armscii8',
2427
			// Chinese-traditional.
2428
			'big5' => 'big5',
2429
			// Chinese-simplified.
2430
			'gbk' => 'gbk',
2431
			// West European.
2432
			'ISO-8859-1' => 'latin1',
2433
			// Romanian.
2434
			'ISO-8859-2' => 'latin2',
2435
			// Turkish.
2436
			'ISO-8859-9' => 'latin5',
2437
			// Latvian
2438
			'ISO-8859-13' => 'latin7',
2439
			// West European with Euro sign.
2440
			'ISO-8859-15' => 'latin9',
2441
			// Thai.
2442
			'tis-620' => 'tis620',
2443
			// Persian, Chinese, etc.
2444
			'UTF-8' => 'utf8',
2445
			// Russian.
2446
			'windows-1251' => 'cp1251',
2447
			// Greek.
2448
			'windows-1253' => 'utf8',
2449
			// Hebrew.
2450
			'windows-1255' => 'utf8',
2451
			// Arabic.
2452
			'windows-1256' => 'cp1256',
2453
		);
2454
2455
		// Get a list of character sets supported by your MySQL server.
2456
		$request = $smcFunc['db_query']('', '
2457
			SHOW CHARACTER SET',
2458
			array(
2459
			)
2460
		);
2461
		$db_charsets = array();
2462
		while ($row = $smcFunc['db_fetch_assoc']($request))
2463
			$db_charsets[] = $row['Charset'];
2464
2465
		$smcFunc['db_free_result']($request);
2466
2467
		// Character sets supported by both MySQL and SMF's language files.
2468
		$charsets = array_intersect($charsets, $db_charsets);
2469
2470
		// Use the messages.body column as indicator for the database charset.
2471
		$request = $smcFunc['db_query']('', '
2472
			SHOW FULL COLUMNS
2473
			FROM {db_prefix}messages
2474
			LIKE {string:body_like}',
2475
			array(
2476
				'body_like' => 'body',
2477
			)
2478
		);
2479
		$column_info = $smcFunc['db_fetch_assoc']($request);
2480
		$smcFunc['db_free_result']($request);
2481
2482
		// A collation looks like latin1_swedish. We only need the character set.
2483
		list($upcontext['database_charset']) = explode('_', $column_info['Collation']);
2484
		$upcontext['database_charset'] = in_array($upcontext['database_charset'], $charsets) ? array_search($upcontext['database_charset'], $charsets) : $upcontext['database_charset'];
2485
2486
		// Detect whether a fulltext index is set.
2487
		$request = $smcFunc['db_query']('', '
2488
 			SHOW INDEX
2489
	  	    FROM {db_prefix}messages',
2490
			array(
2491
			)
2492
		);
2493
2494
		$upcontext['dropping_index'] = false;
2495
2496
		// If there's a fulltext index, we need to drop it first...
2497 View Code Duplication
		if ($request !== false || $smcFunc['db_num_rows']($request) != 0)
0 ignored issues
show
Duplication introduced by
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...
2498
		{
2499
			while ($row = $smcFunc['db_fetch_assoc']($request))
2500
				if ($row['Column_name'] == 'body' && (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT' || isset($row['Comment']) && $row['Comment'] == 'FULLTEXT'))
2501
					$upcontext['fulltext_index'][] = $row['Key_name'];
2502
			$smcFunc['db_free_result']($request);
2503
2504
			if (isset($upcontext['fulltext_index']))
2505
				$upcontext['fulltext_index'] = array_unique($upcontext['fulltext_index']);
2506
		}
2507
2508
		// Drop it and make a note...
2509
		if (!empty($upcontext['fulltext_index']))
2510
		{
2511
			$upcontext['dropping_index'] = true;
2512
2513
			$smcFunc['db_query']('', '
2514
  			ALTER TABLE {db_prefix}messages
2515
	  		DROP INDEX ' . implode(',
2516
		  	DROP INDEX ', $upcontext['fulltext_index']),
2517
				array(
2518
					'db_error_skip' => true,
2519
				)
2520
			);
2521
2522
			// Update the settings table
2523
			$smcFunc['db_insert']('replace',
2524
				'{db_prefix}settings',
2525
				array('variable' => 'string', 'value' => 'string'),
2526
				array('db_search_index', ''),
2527
				array('variable')
2528
			);
2529
		}
2530
2531
		// Figure out what charset we should be converting from...
2532
		$lang_charsets = array(
2533
			'arabic' => 'windows-1256',
2534
			'armenian_east' => 'armscii-8',
2535
			'armenian_west' => 'armscii-8',
2536
			'azerbaijani_latin' => 'ISO-8859-9',
2537
			'bangla' => 'UTF-8',
2538
			'belarusian' => 'ISO-8859-5',
2539
			'bulgarian' => 'windows-1251',
2540
			'cambodian' => 'UTF-8',
2541
			'chinese_simplified' => 'gbk',
2542
			'chinese_traditional' => 'big5',
2543
			'croation' => 'ISO-8859-2',
2544
			'czech' => 'ISO-8859-2',
2545
			'czech_informal' => 'ISO-8859-2',
2546
			'english_pirate' => 'UTF-8',
2547
			'esperanto' => 'ISO-8859-3',
2548
			'estonian' => 'ISO-8859-15',
2549
			'filipino_tagalog' => 'UTF-8',
2550
			'filipino_vasayan' => 'UTF-8',
2551
			'georgian' => 'UTF-8',
2552
			'greek' => 'ISO-8859-3',
2553
			'hebrew' => 'windows-1255',
2554
			'hungarian' => 'ISO-8859-2',
2555
			'irish' => 'UTF-8',
2556
			'japanese' => 'UTF-8',
2557
			'khmer' => 'UTF-8',
2558
			'korean' => 'UTF-8',
2559
			'kurdish_kurmanji' => 'ISO-8859-9',
2560
			'kurdish_sorani' => 'windows-1256',
2561
			'lao' => 'tis-620',
2562
			'latvian' => 'ISO-8859-13',
2563
			'lithuanian' => 'ISO-8859-4',
2564
			'macedonian' => 'UTF-8',
2565
			'malayalam' => 'UTF-8',
2566
			'mongolian' => 'UTF-8',
2567
			'nepali' => 'UTF-8',
2568
			'persian' => 'UTF-8',
2569
			'polish' => 'ISO-8859-2',
2570
			'romanian' => 'ISO-8859-2',
2571
			'russian' => 'windows-1252',
2572
			'sakha' => 'UTF-8',
2573
			'serbian_cyrillic' => 'ISO-8859-5',
2574
			'serbian_latin' => 'ISO-8859-2',
2575
			'sinhala' => 'UTF-8',
2576
			'slovak' => 'ISO-8859-2',
2577
			'slovenian' => 'ISO-8859-2',
2578
			'telugu' => 'UTF-8',
2579
			'thai' => 'tis-620',
2580
			'turkish' => 'ISO-8859-9',
2581
			'turkmen' => 'ISO-8859-9',
2582
			'ukranian' => 'windows-1251',
2583
			'urdu' => 'UTF-8',
2584
			'uzbek_cyrillic' => 'ISO-8859-5',
2585
			'uzbek_latin' => 'ISO-8859-5',
2586
			'vietnamese' => 'UTF-8',
2587
			'yoruba' => 'UTF-8'
2588
		);
2589
2590
		// Default to ISO-8859-1 unless we detected another supported charset
2591
		$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';
2592
2593
		$upcontext['charset_list'] = array_keys($charsets);
2594
2595
		// Translation table for the character sets not native for MySQL.
2596
		$translation_tables = array(
2597
			'windows-1255' => array(
2598
				'0x81' => '\'\'',		'0x8A' => '\'\'',		'0x8C' => '\'\'',
2599
				'0x8D' => '\'\'',		'0x8E' => '\'\'',		'0x8F' => '\'\'',
2600
				'0x90' => '\'\'',		'0x9A' => '\'\'',		'0x9C' => '\'\'',
2601
				'0x9D' => '\'\'',		'0x9E' => '\'\'',		'0x9F' => '\'\'',
2602
				'0xCA' => '\'\'',		'0xD9' => '\'\'',		'0xDA' => '\'\'',
2603
				'0xDB' => '\'\'',		'0xDC' => '\'\'',		'0xDD' => '\'\'',
2604
				'0xDE' => '\'\'',		'0xDF' => '\'\'',		'0xFB' => '0xD792',
2605
				'0xFC' => '0xE282AC',		'0xFF' => '0xD6B2',		'0xC2' => '0xFF',
2606
				'0x80' => '0xFC',		'0xE2' => '0xFB',		'0xA0' => '0xC2A0',
2607
				'0xA1' => '0xC2A1',		'0xA2' => '0xC2A2',		'0xA3' => '0xC2A3',
2608
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2609
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2610
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2611
				'0xAF' => '0xC2AF',		'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',
2612
				'0xB2' => '0xC2B2',		'0xB3' => '0xC2B3',		'0xB4' => '0xC2B4',
2613
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2614
				'0xB8' => '0xC2B8',		'0xB9' => '0xC2B9',		'0xBB' => '0xC2BB',
2615
				'0xBC' => '0xC2BC',		'0xBD' => '0xC2BD',		'0xBE' => '0xC2BE',
2616
				'0xBF' => '0xC2BF',		'0xD7' => '0xD7B3',		'0xD1' => '0xD781',
2617
				'0xD4' => '0xD7B0',		'0xD5' => '0xD7B1',		'0xD6' => '0xD7B2',
2618
				'0xE0' => '0xD790',		'0xEA' => '0xD79A',		'0xEC' => '0xD79C',
2619
				'0xED' => '0xD79D',		'0xEE' => '0xD79E',		'0xEF' => '0xD79F',
2620
				'0xF0' => '0xD7A0',		'0xF1' => '0xD7A1',		'0xF2' => '0xD7A2',
2621
				'0xF3' => '0xD7A3',		'0xF5' => '0xD7A5',		'0xF6' => '0xD7A6',
2622
				'0xF7' => '0xD7A7',		'0xF8' => '0xD7A8',		'0xF9' => '0xD7A9',
2623
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2624
				'0x86' => '0xE280A0',	'0x87' => '0xE280A1',	'0x89' => '0xE280B0',
2625
				'0x8B' => '0xE280B9',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2626
				'0x95' => '0xE280A2',	'0x97' => '0xE28094',	'0x99' => '0xE284A2',
2627
				'0xC0' => '0xD6B0',		'0xC1' => '0xD6B1',		'0xC3' => '0xD6B3',
2628
				'0xC4' => '0xD6B4',		'0xC5' => '0xD6B5',		'0xC6' => '0xD6B6',
2629
				'0xC7' => '0xD6B7',		'0xC8' => '0xD6B8',		'0xC9' => '0xD6B9',
2630
				'0xCB' => '0xD6BB',		'0xCC' => '0xD6BC',		'0xCD' => '0xD6BD',
2631
				'0xCE' => '0xD6BE',		'0xCF' => '0xD6BF',		'0xD0' => '0xD780',
2632
				'0xD2' => '0xD782',		'0xE3' => '0xD793',		'0xE4' => '0xD794',
2633
				'0xE5' => '0xD795',		'0xE7' => '0xD797',		'0xE9' => '0xD799',
2634
				'0xFD' => '0xE2808E',	'0xFE' => '0xE2808F',	'0x92' => '0xE28099',
2635
				'0x83' => '0xC692',		'0xD3' => '0xD783',		'0x88' => '0xCB86',
2636
				'0x98' => '0xCB9C',		'0x91' => '0xE28098',	'0x96' => '0xE28093',
2637
				'0xBA' => '0xC3B7',		'0x9B' => '0xE280BA',	'0xAA' => '0xC397',
2638
				'0xA4' => '0xE282AA',	'0xE1' => '0xD791',		'0xE6' => '0xD796',
2639
				'0xE8' => '0xD798',		'0xEB' => '0xD79B',		'0xF4' => '0xD7A4',
2640
				'0xFA' => '0xD7AA',
2641
			),
2642
			'windows-1253' => array(
2643
				'0x81' => '\'\'',			'0x88' => '\'\'',			'0x8A' => '\'\'',
2644
				'0x8C' => '\'\'',			'0x8D' => '\'\'',			'0x8E' => '\'\'',
2645
				'0x8F' => '\'\'',			'0x90' => '\'\'',			'0x98' => '\'\'',
2646
				'0x9A' => '\'\'',			'0x9C' => '\'\'',			'0x9D' => '\'\'',
2647
				'0x9E' => '\'\'',			'0x9F' => '\'\'',			'0xAA' => '\'\'',
2648
				'0xD2' => '0xE282AC',			'0xFF' => '0xCE92',			'0xCE' => '0xCE9E',
2649
				'0xB8' => '0xCE88',		'0xBA' => '0xCE8A',		'0xBC' => '0xCE8C',
2650
				'0xBE' => '0xCE8E',		'0xBF' => '0xCE8F',		'0xC0' => '0xCE90',
2651
				'0xC8' => '0xCE98',		'0xCA' => '0xCE9A',		'0xCC' => '0xCE9C',
2652
				'0xCD' => '0xCE9D',		'0xCF' => '0xCE9F',		'0xDA' => '0xCEAA',
2653
				'0xE8' => '0xCEB8',		'0xEA' => '0xCEBA',		'0xEC' => '0xCEBC',
2654
				'0xEE' => '0xCEBE',		'0xEF' => '0xCEBF',		'0xC2' => '0xFF',
2655
				'0xBD' => '0xC2BD',		'0xED' => '0xCEBD',		'0xB2' => '0xC2B2',
2656
				'0xA0' => '0xC2A0',		'0xA3' => '0xC2A3',		'0xA4' => '0xC2A4',
2657
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2658
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2659
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2660
				'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',		'0xB3' => '0xC2B3',
2661
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2662
				'0xBB' => '0xC2BB',		'0xE2' => '0xCEB2',		'0x80' => '0xD2',
2663
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2664
				'0x86' => '0xE280A0',	'0xA1' => '0xCE85',		'0xA2' => '0xCE86',
2665
				'0x87' => '0xE280A1',	'0x89' => '0xE280B0',	'0xB9' => '0xCE89',
2666
				'0x8B' => '0xE280B9',	'0x91' => '0xE28098',	'0x99' => '0xE284A2',
2667
				'0x92' => '0xE28099',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2668
				'0x95' => '0xE280A2',	'0x96' => '0xE28093',	'0x97' => '0xE28094',
2669
				'0x9B' => '0xE280BA',	'0xAF' => '0xE28095',	'0xB4' => '0xCE84',
2670
				'0xC1' => '0xCE91',		'0xC3' => '0xCE93',		'0xC4' => '0xCE94',
2671
				'0xC5' => '0xCE95',		'0xC6' => '0xCE96',		'0x83' => '0xC692',
2672
				'0xC7' => '0xCE97',		'0xC9' => '0xCE99',		'0xCB' => '0xCE9B',
2673
				'0xD0' => '0xCEA0',		'0xD1' => '0xCEA1',		'0xD3' => '0xCEA3',
2674
				'0xD4' => '0xCEA4',		'0xD5' => '0xCEA5',		'0xD6' => '0xCEA6',
2675
				'0xD7' => '0xCEA7',		'0xD8' => '0xCEA8',		'0xD9' => '0xCEA9',
2676
				'0xDB' => '0xCEAB',		'0xDC' => '0xCEAC',		'0xDD' => '0xCEAD',
2677
				'0xDE' => '0xCEAE',		'0xDF' => '0xCEAF',		'0xE0' => '0xCEB0',
2678
				'0xE1' => '0xCEB1',		'0xE3' => '0xCEB3',		'0xE4' => '0xCEB4',
2679
				'0xE5' => '0xCEB5',		'0xE6' => '0xCEB6',		'0xE7' => '0xCEB7',
2680
				'0xE9' => '0xCEB9',		'0xEB' => '0xCEBB',		'0xF0' => '0xCF80',
2681
				'0xF1' => '0xCF81',		'0xF2' => '0xCF82',		'0xF3' => '0xCF83',
2682
				'0xF4' => '0xCF84',		'0xF5' => '0xCF85',		'0xF6' => '0xCF86',
2683
				'0xF7' => '0xCF87',		'0xF8' => '0xCF88',		'0xF9' => '0xCF89',
2684
				'0xFA' => '0xCF8A',		'0xFB' => '0xCF8B',		'0xFC' => '0xCF8C',
2685
				'0xFD' => '0xCF8D',		'0xFE' => '0xCF8E',
2686
			),
2687
		);
2688
2689
		// Make some preparations.
2690
		if (isset($translation_tables[$upcontext['charset_detected']]))
2691
		{
2692
			$replace = '%field%';
2693
2694
			// Build a huge REPLACE statement...
2695
			foreach ($translation_tables[$upcontext['charset_detected']] as $from => $to)
2696
				$replace = 'REPLACE(' . $replace . ', ' . $from . ', ' . $to . ')';
2697
		}
2698
2699
		// Get a list of table names ahead of time... This makes it easier to set our substep and such
2700
		db_extend();
2701
		$queryTables = $smcFunc['db_list_tables'](false, $db_prefix . '%');
2702
2703
		$upcontext['table_count'] = count($queryTables);
2704
	
2705
		// What ones have we already done?
2706
		foreach ($queryTables as $id => $table)
2707
			if ($id < $_GET['substep'])
2708
				$upcontext['previous_tables'][] = $table;
2709
2710
		$upcontext['cur_table_num'] = $_GET['substep'];
2711
		$upcontext['cur_table_name'] = str_replace($db_prefix, '', $queryTables[$_GET['substep']]);
2712
		$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2713
			
2714
		// Make sure we're ready & have painted the template before proceeding	
2715
		if ($support_js && !isset($_GET['xml'])) {
2716
			$_GET['substep'] = 0;
2717
			return false;
2718
		}	
2719
			
2720
		// We want to start at the first table.
2721
		for ($substep = $_GET['substep'], $n = count($queryTables); $substep < $n; $substep++)
2722
		{
2723
			$table = $queryTables[$substep];
2724
2725
			$getTableStatus = $smcFunc['db_query']('', '
2726
				SHOW TABLE STATUS
2727
				LIKE {string:table_name}',
2728
				array(
2729
					'table_name' => str_replace('_', '\_', $table)
2730
				)
2731
			);
2732
2733
			// Only one row so we can just fetch_assoc and free the result...
2734
			$table_info = $smcFunc['db_fetch_assoc']($getTableStatus);
2735
			$smcFunc['db_free_result']($getTableStatus);
2736
2737
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($queryTables[$substep + 1]) ? $queryTables[$substep + 1] : $queryTables[$substep]));
2738
			$upcontext['cur_table_num'] = $substep + 1;
2739
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2740
2741
			// Do we need to pause?
2742
			nextSubstep($substep);
2743
2744
			// Just to make sure it doesn't time out.
2745
			if (function_exists('apache_reset_timeout'))
2746
				@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...
2747
2748
			$table_charsets = array();
2749
2750
			// Loop through each column.
2751
			$queryColumns = $smcFunc['db_query']('', '
2752
				SHOW FULL COLUMNS
2753
				FROM ' . $table_info['Name'],
2754
				array(
2755
				)
2756
			);
2757
			while ($column_info = $smcFunc['db_fetch_assoc']($queryColumns))
2758
			{
2759
				// Only text'ish columns have a character set and need converting.
2760
				if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false)
2761
				{
2762
					$collation = empty($column_info['Collation']) || $column_info['Collation'] === 'NULL' ? $table_info['Collation'] : $column_info['Collation'];
2763
					if (!empty($collation) && $collation !== 'NULL')
2764
					{
2765
						list($charset) = explode('_', $collation);
2766
2767
						if (!isset($table_charsets[$charset]))
2768
							$table_charsets[$charset] = array();
2769
2770
						$table_charsets[$charset][] = $column_info;
2771
					}
2772
				}
2773
			}
2774
			$smcFunc['db_free_result']($queryColumns);
2775
2776
			// Only change the column if the data doesn't match the current charset.
2777
			if ((count($table_charsets) === 1 && key($table_charsets) !== $charsets[$upcontext['charset_detected']]) || count($table_charsets) > 1)
2778
			{
2779
				$updates_blob = '';
2780
				$updates_text = '';
2781
				foreach ($table_charsets as $charset => $columns)
2782
				{
2783
					if ($charset !== $charsets[$upcontext['charset_detected']])
2784
					{
2785
						foreach ($columns as $column)
2786
						{
2787
							$updates_blob .= '
2788
								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'] . '\'') . ',';
2789
							$updates_text .= '
2790
								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'] . '\'') . ',';
2791
						}
2792
					}
2793
				}
2794
2795
				// Change the columns to binary form.
2796
				$smcFunc['db_query']('', '
2797
					ALTER TABLE {raw:table_name}{raw:updates_blob}',
2798
					array(
2799
						'table_name' => $table_info['Name'],
2800
						'updates_blob' => substr($updates_blob, 0, -1),
2801
					)
2802
				);
2803
2804
				// Convert the character set if MySQL has no native support for it.
2805
				if (isset($translation_tables[$upcontext['charset_detected']]))
2806
				{
2807
					$update = '';
2808
					foreach ($table_charsets as $charset => $columns)
2809
						foreach ($columns as $column)
2810
							$update .= '
2811
								' . $column['Field'] . ' = ' . strtr($replace, array('%field%' => $column['Field'])) . ',';
0 ignored issues
show
Bug introduced by
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...
2812
2813
					$smcFunc['db_query']('', '
2814
						UPDATE {raw:table_name}
2815
						SET {raw:updates}',
2816
						array(
2817
							'table_name' => $table_info['Name'],
2818
							'updates' => substr($update, 0, -1),
2819
						)
2820
					);
2821
				}
2822
2823
				// Change the columns back, but with the proper character set.
2824
				$smcFunc['db_query']('', '
2825
					ALTER TABLE {raw:table_name}{raw:updates_text}',
2826
					array(
2827
						'table_name' => $table_info['Name'],
2828
						'updates_text' => substr($updates_text, 0, -1),
2829
					)
2830
				);
2831
			}
2832
2833
			// Now do the actual conversion (if still needed).
2834
			if ($charsets[$upcontext['charset_detected']] !== 'utf8')
2835
			{
2836
				if ($command_line)
2837
					echo 'Converting table ' . $table_info['Name'] . ' to UTF-8...';
2838
2839
				$smcFunc['db_query']('', '
2840
					ALTER TABLE {raw:table_name}
2841
					CONVERT TO CHARACTER SET utf8',
2842
					array(
2843
						'table_name' => $table_info['Name'],
2844
					)
2845
				);
2846
2847
				if ($command_line)
2848
					echo " done.\n";
2849
			}
2850
			// If this is XML to keep it nice for the user do one table at a time anyway!
2851
			if (isset($_GET['xml']))
2852
				return upgradeExit();
2853
		}
2854
2855
		$prev_charset = empty($translation_tables[$upcontext['charset_detected']]) ? $charsets[$upcontext['charset_detected']] : $translation_tables[$upcontext['charset_detected']];
2856
2857
		$smcFunc['db_insert']('replace',
2858
			'{db_prefix}settings',
2859
			array('variable' => 'string', 'value' => 'string'),
2860
			array(array('global_character_set', 'UTF-8'), array('previousCharacterSet', $prev_charset)),
2861
			array('variable')
2862
		);
2863
2864
		// Store it in Settings.php too because it's needed before db connection.
2865
		// Hopefully this works...
2866
		require_once($sourcedir . '/Subs-Admin.php');
2867
		updateSettingsFile(array('db_character_set' => '\'utf8\''));
2868
2869
		// The conversion might have messed up some serialized strings. Fix them!
2870
		$request = $smcFunc['db_query']('', '
2871
			SELECT id_action, extra
2872
			FROM {db_prefix}log_actions
2873
			WHERE action IN ({string:remove}, {string:delete})',
2874
			array(
2875
				'remove' => 'remove',
2876
				'delete' => 'delete',
2877
			)
2878
		);
2879 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Duplication introduced by
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...
2880
		{
2881
			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)
2882
				$smcFunc['db_query']('', '
2883
					UPDATE {db_prefix}log_actions
2884
					SET extra = {string:extra}
2885
					WHERE id_action = {int:current_action}',
2886
					array(
2887
						'current_action' => $row['id_action'],
2888
						'extra' => $matches[1] . strlen($matches[3]) . ':"' . $matches[3] . '"' . $matches[4],
2889
					)
2890
				);
2891
		}
2892
		$smcFunc['db_free_result']($request);
2893
2894
		if ($upcontext['dropping_index'] && $command_line)
2895
		{
2896
			echo "\nYour fulltext search index was dropped to facilitate the conversion. You will need to recreate it.";
2897
			flush();
2898
		}
2899
	}
2900
	$_GET['substep'] = 0;
2901
	return false;
2902
}
2903
2904
function serialize_to_json()
2905
{
2906
	global $command_line, $smcFunc, $modSettings, $sourcedir, $upcontext, $support_js;
2907
2908
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'serialize_json_xml' : 'serialize_json';
2909
	// First thing's first - did we already do this?
2910
	if (!empty($modSettings['json_done']))
2911
	{
2912
		if ($command_line)
2913
			return DeleteUpgrade();
2914
		else
2915
			return true;
2916
	}
2917
2918
	// Done it already - js wise?
2919
	if (!empty($_POST['json_done']))
2920
		return true;
2921
2922
	// List of tables affected by this function
2923
	// 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...
2924
	// If 3rd item in array is true, it indicates that col1 could be empty...
2925
	$tables = array(
2926
		'background_tasks' => array('id_task', 'task_data'),
2927
		'log_actions' => array('id_action', 'extra'),
2928
		'log_online' => array('session', 'url'),
2929
		'log_packages' => array('id_install', 'db_changes', 'failed_steps', 'credits'),
2930
		'log_spider_hits' => array('id_hit', 'url'),
2931
		'log_subscribed' => array('id_sublog', 'pending_details'),
2932
		'pm_rules' => array('id_rule', 'criteria', 'actions'),
2933
		'qanda' => array('id_question', 'answers'),
2934
		'subscriptions' => array('id_subscribe', 'cost'),
2935
		'user_alerts' => array('id_alert', 'extra', true),
2936
		'user_drafts' => array('id_draft', 'to_list', true),
2937
		// These last two are a bit different - we'll handle those separately
2938
		'settings' => array(),
2939
		'themes' => array()
2940
	);
2941
2942
	// Set up some context stuff...
2943
	// Because we're not using numeric indices, we need this to figure out the current table name...
2944
	$keys = array_keys($tables);
2945
2946
	$upcontext['page_title'] = 'Converting to JSON';
2947
	$upcontext['table_count'] = count($keys);
2948
	$upcontext['cur_table_num'] = $_GET['substep'];
2949
	$upcontext['cur_table_name'] = isset($keys[$_GET['substep']]) ? $keys[$_GET['substep']] : $keys[0];
2950
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2951
2952 View Code Duplication
	foreach ($keys as $id => $table)
0 ignored issues
show
Duplication introduced by
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...
2953
		if ($id < $_GET['substep'])
2954
			$upcontext['previous_tables'][] = $table;
2955
2956
	if ($command_line)
2957
		echo 'Converting data from serialize() to json_encode().';
2958
2959
	if (!$support_js || isset($_GET['xml']))
2960
	{
2961
		// Fix the data in each table
2962
		for ($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
2963
		{
2964
			$upcontext['cur_table_name'] = isset($keys[$substep + 1]) ? $keys[$substep + 1] : $keys[$substep];
2965
			$upcontext['cur_table_num'] = $substep + 1;
2966
2967
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2968
2969
			// Do we need to pause?
2970
			nextSubstep($substep);
2971
2972
			// Initialize a few things...
2973
			$where = '';
2974
			$vars = array();
2975
			$table = $keys[$substep];
2976
			$info = $tables[$table];
2977
2978
			// Now the fun - build our queries and all that fun stuff
2979
			if ($table == 'settings')
2980
			{
2981
				// Now a few settings...
2982
				$serialized_settings = array(
2983
					'attachment_basedirectories',
2984
					'attachmentUploadDir',
2985
					'cal_today_birthday',
2986
					'cal_today_event',
2987
					'cal_today_holiday',
2988
					'displayFields',
2989
					'last_attachments_directory',
2990
					'memberlist_cache',
2991
					'search_index_custom_config',
2992
					'spider_name_cache'
2993
				);
2994
2995
				// Loop through and fix these...
2996
				$new_settings = array();
2997
				if ($command_line)
2998
					echo "\n" . 'Fixing some settings...';
2999
3000
				foreach ($serialized_settings as $var)
3001
				{
3002
					if (isset($modSettings[$var]))
3003
					{
3004
						// Attempt to unserialize the setting
3005
						$temp = @safe_unserialize($modSettings[$var]);
3006
						if (!$temp && $command_line)
3007
							echo "\n - Failed to unserialize the '" . $var . "' setting. Skipping.";
3008
						elseif ($temp !== false)
3009
							$new_settings[$var] = json_encode($temp);
3010
					}
3011
				}
3012
3013
				// Update everything at once
3014
				if (!function_exists('cache_put_data'))
3015
					require_once($sourcedir . '/Load.php');
3016
				updateSettings($new_settings, true);
3017
3018
				if ($command_line)
3019
					echo ' done.';
3020
			}
3021
			elseif ($table == 'themes')
3022
			{
3023
				// Finally, fix the admin prefs. Unfortunately this is stored per theme, but hopefully they only have one theme installed at this point...
3024
				$query = $smcFunc['db_query']('', '
3025
					SELECT id_member, id_theme, value FROM {db_prefix}themes
3026
					WHERE variable = {string:admin_prefs}',
3027
						array(
3028
							'admin_prefs' => 'admin_preferences'
3029
						)
3030
				);
3031
3032
				if ($smcFunc['db_num_rows']($query) != 0)
3033
				{
3034
					while ($row = $smcFunc['db_fetch_assoc']($query))
3035
					{
3036
						$temp = @safe_unserialize($row['value']);
3037
3038
						if ($command_line)
3039
						{
3040
							if ($temp === false)
3041
								echo "\n" . 'Unserialize of admin_preferences for user ' . $row['id_member'] . ' failed. Skipping.';
3042
							else
3043
								echo "\n" . 'Fixing admin preferences...';
3044
						}
3045
3046
						if ($temp !== false)
3047
						{
3048
							$row['value'] = json_encode($temp);
3049
3050
							// Even though we have all values from the table, UPDATE is still faster than REPLACE
3051
							$smcFunc['db_query']('', '
3052
								UPDATE {db_prefix}themes
3053
								SET value = {string:prefs}
3054
								WHERE id_theme = {int:theme}
3055
									AND id_member = {int:member}
3056
									AND variable = {string:admin_prefs}',
3057
								array(
3058
									'prefs' => $row['value'],
3059
									'theme' => $row['id_theme'],
3060
									'member' => $row['id_member'],
3061
									'admin_prefs' => 'admin_preferences'
3062
								)
3063
							);
3064
3065
							if ($command_line)
3066
								echo ' done.';
3067
						}
3068
					}
3069
3070
					$smcFunc['db_free_result']($query);
3071
				}
3072
			}
3073
			else
3074
			{
3075
				// First item is always the key...
3076
				$key = $info[0];
3077
				unset($info[0]);
3078
3079
				// Now we know what columns we have and such...
3080
				if (count($info) == 2 && $info[2] === true)
3081
				{
3082
					$col_select = $info[1];
3083
					$where = ' WHERE ' . $info[1] . ' != {empty}';
3084
				}
3085
				else
3086
				{
3087
					$col_select = implode(', ', $info);
3088
				}
3089
3090
				$query = $smcFunc['db_query']('', '
3091
					SELECT ' . $key . ', ' . $col_select . '
3092
					FROM {db_prefix}' . $table . $where,
3093
					array()
3094
				);
3095
3096
				if ($smcFunc['db_num_rows']($query) != 0)
3097
				{
3098
					if ($command_line)
3099
					{
3100
						echo "\n" . ' +++ Fixing the "' . $table . '" table...';
3101
						flush();
3102
					}
3103
3104
					while ($row = $smcFunc['db_fetch_assoc']($query))
3105
					{
3106
						$update = '';
3107
3108
						// We already know what our key is...
3109
						foreach ($info as $col)
3110
						{
3111
							if ($col !== true && $row[$col] != '')
3112
							{
3113
								$temp = @safe_unserialize($row[$col]);
3114
3115
								if ($temp === false && $command_line)
3116
								{
3117
									echo "\nFailed to unserialize " . $row[$col] . "... Skipping\n";
3118
								}
3119
								else
3120
								{
3121
									$row[$col] = json_encode($temp);
3122
3123
									// Build our SET string and variables array
3124
									$update .= (empty($update) ? '' : ', ') . $col . ' = {string:' . $col . '}';
3125
									$vars[$col] = $row[$col];
3126
								}
3127
							}
3128
						}
3129
3130
						$vars[$key] = $row[$key];
3131
3132
						// In a few cases, we might have empty data, so don't try to update in those situations...
3133
						if (!empty($update))
3134
						{
3135
							$smcFunc['db_query']('', '
3136
								UPDATE {db_prefix}' . $table . '
3137
								SET ' . $update . '
3138
								WHERE ' . $key . ' = {' . ($key == 'session' ? 'string' : 'int') . ':' . $key . '}',
3139
								$vars
3140
							);
3141
						}
3142
					}
3143
3144
					if ($command_line)
3145
						echo ' done.';
3146
3147
					// Free up some memory...
3148
					$smcFunc['db_free_result']($query);
3149
				}
3150
			}
3151
			// If this is XML to keep it nice for the user do one table at a time anyway!
3152
			if (isset($_GET['xml']))
3153
				return upgradeExit();
3154
		}
3155
3156
		if ($command_line)
3157
		{
3158
			echo "\n" . 'Successful.' . "\n";
3159
			flush();
3160
		}
3161
		$upcontext['step_progress'] = 100;
3162
3163
		// Last but not least, insert a dummy setting so we don't have to do this again in the future...
3164
		updateSettings(array('json_done' => true));
3165
3166
		$_GET['substep'] = 0;
3167
		// Make sure we move on!
3168
		if ($command_line)
3169
			return DeleteUpgrade();
3170
3171
		return true;
3172
	}
3173
3174
	// If this fails we just move on to deleting the upgrade anyway...
3175
	$_GET['substep'] = 0;
3176
	return false;
3177
}
3178
3179
/******************************************************************************
3180
******************* Templates are below this point ****************************
3181
******************************************************************************/
3182
3183
// This is what is displayed if there's any chmod to be done. If not it returns nothing...
3184
function template_chmod()
3185
{
3186
	global $upcontext, $txt, $settings;
3187
3188
	// Don't call me twice!
3189
	if (!empty($upcontext['chmod_called']))
3190
		return;
3191
3192
	$upcontext['chmod_called'] = true;
3193
3194
	// Nothing?
3195
	if (empty($upcontext['chmod']['files']) && empty($upcontext['chmod']['ftp_error']))
3196
		return;
3197
3198
	// Was it a problem with Windows?
3199
	if (!empty($upcontext['chmod']['ftp_error']) && $upcontext['chmod']['ftp_error'] == 'total_mess')
3200
	{
3201
		echo '
3202
			<div class="error_message red">
3203
				The following files need to be writable to continue the upgrade. Please ensure the Windows permissions are correctly set to allow this:<br>
3204
				<ul style="margin: 2.5ex; font-family: monospace;">
3205
					<li>' . implode('</li>
3206
					<li>', $upcontext['chmod']['files']) . '</li>
3207
				</ul>
3208
			</div>';
3209
3210
		return false;
3211
	}
3212
3213
	echo '
3214
		<div class="panel">
3215
			<h2>Your FTP connection information</h2>
3216
			<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>
3217
			<script>
3218
				function warning_popup()
3219
				{
3220
					popup = window.open(\'\',\'popup\',\'height=150,width=400,scrollbars=yes\');
3221
					var content = popup.document;
3222
					content.write(\'<!DOCTYPE html>\n\');
3223
					content.write(\'<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>\n\t<head>\n\t\t<meta name="robots" content="noindex">\n\t\t\');
3224
					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\');
3225
					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\');
3226
					content.write(\'<p>', implode('<br>\n\t\t\t', $upcontext['chmod']['files']), '</p>\n\t\t\t\');';
3227
3228
	if (isset($upcontext['systemos']) && $upcontext['systemos'] == 'linux')
3229
		echo '
3230
					content.write(\'<hr>\n\t\t\t\');
3231
					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\');
3232
					content.write(\'<tt># chmod a+w ', implode(' ', $upcontext['chmod']['files']), '</tt>\n\t\t\t\');';
3233
3234
	echo '
3235
					content.write(\'<a href="javascript:self.close();">close</a>\n\t\t</div>\n\t</body>\n</html>\');
3236
					content.close();
3237
				}
3238
			</script>';
3239
3240
	if (!empty($upcontext['chmod']['ftp_error']))
3241
		echo '
3242
			<div class="error_message red">
3243
				The following error was encountered when trying to connect:<br><br>
3244
				<code>', $upcontext['chmod']['ftp_error'], '</code>
3245
			</div>
3246
			<br>';
3247
3248
	if (empty($upcontext['chmod_in_form']))
3249
		echo '
3250
	<form action="', $upcontext['form_url'], '" method="post">';
3251
3252
	echo '
3253
		<table width="520" border="0" align="center" style="margin-bottom: 1ex;">
3254
			<tr>
3255
				<td width="26%" valign="top" class="textbox"><label for="ftp_server">', $txt['ftp_server'], ':</label></td>
3256
				<td>
3257
					<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>
3258
					<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">
3259
					<div class="smalltext block">', $txt['ftp_server_info'], '</div>
3260
				</td>
3261
			</tr><tr>
3262
				<td width="26%" valign="top" class="textbox"><label for="ftp_username">', $txt['ftp_username'], ':</label></td>
3263
				<td>
3264
					<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">
3265
					<div class="smalltext block">', $txt['ftp_username_info'], '</div>
3266
				</td>
3267
			</tr><tr>
3268
				<td width="26%" valign="top" class="textbox"><label for="ftp_password">', $txt['ftp_password'], ':</label></td>
3269
				<td>
3270
					<input type="password" size="50" name="ftp_password" id="ftp_password" style="width: 99%;" class="input_password">
3271
					<div class="smalltext block">', $txt['ftp_password_info'], '</div>
3272
				</td>
3273
			</tr><tr>
3274
				<td width="26%" valign="top" class="textbox"><label for="ftp_path">', $txt['ftp_path'], ':</label></td>
3275
				<td style="padding-bottom: 1ex;">
3276
					<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">
3277
					<div class="smalltext block">', !empty($upcontext['chmod']['path']) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'], '</div>
3278
				</td>
3279
			</tr>
3280
		</table>
3281
3282
		<div class="righttext" style="margin: 1ex;"><input type="submit" value="', $txt['ftp_connect'], '" class="button_submit"></div>
3283
	</div>';
3284
3285
	if (empty($upcontext['chmod_in_form']))
3286
		echo '
3287
	</form>';
3288
}
3289
3290
function template_upgrade_above()
3291
{
3292
	global $modSettings, $txt, $settings, $upcontext, $upgradeurl;
3293
3294
	echo '<!DOCTYPE html>
3295
<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>
3296
	<head>
3297
		<meta charset="', isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8', '">
3298
		<meta name="robots" content="noindex">
3299
		<title>', $txt['upgrade_upgrade_utility'], '</title>
3300
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/index.css?alp21">
3301
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/install.css?alp21">
3302
		', $txt['lang_rtl'] == true ? '<link rel="stylesheet" href="' . $settings['default_theme_url'] . '/css/rtl.css?alp21">' : '', '
3303
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
3304
		<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
3305
		<script>
3306
			var smf_scripturl = \'', $upgradeurl, '\';
3307
			var smf_charset = \'', (empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'UTF-8' : $txt['lang_character_set']) : $modSettings['global_character_set']), '\';
3308
			var startPercent = ', $upcontext['overall_percent'], ';
3309
3310
			// This function dynamically updates the step progress bar - and overall one as required.
3311
			function updateStepProgress(current, max, overall_weight)
3312
			{
3313
				// What out the actual percent.
3314
				var width = parseInt((current / max) * 100);
3315
				if (document.getElementById(\'step_progress\'))
3316
				{
3317
					document.getElementById(\'step_progress\').style.width = width + "%";
3318
					setInnerHTML(document.getElementById(\'step_text\'), width + "%");
3319
				}
3320
				if (overall_weight && document.getElementById(\'overall_progress\'))
3321
				{
3322
					overall_width = parseInt(startPercent + width * (overall_weight / 100));
3323
					document.getElementById(\'overall_progress\').style.width = overall_width + "%";
3324
					setInnerHTML(document.getElementById(\'overall_text\'), overall_width + "%");
3325
				}
3326
			}
3327
		</script>
3328
	</head>
3329
	<body>
3330
	<div id="footerfix">
3331
		<div id="header">
3332
			<h1 class="forumtitle">', $txt['upgrade_upgrade_utility'], '</h1>
3333
			<img id="smflogo" src="', $settings['default_theme_url'], '/images/smflogo.png" alt="Simple Machines Forum" title="Simple Machines Forum">
3334
		</div>
3335
	<div id="wrapper">
3336
		<div id="upper_section">
3337
			<div id="inner_section">
3338
				<div id="inner_wrap">
3339
				</div>
3340
			</div>
3341
		</div>
3342
		<div id="content_section">
3343
		<div id="main_content_section">
3344
			<div id="main_steps">
3345
				<h2>', $txt['upgrade_progress'], '</h2>
3346
				<ul>';
3347
3348 View Code Duplication
	foreach ($upcontext['steps'] as $num => $step)
0 ignored issues
show
Duplication introduced by
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...
3349
		echo '
3350
						<li class="', $num < $upcontext['current_step'] ? 'stepdone' : ($num == $upcontext['current_step'] ? 'stepcurrent' : 'stepwaiting'), '">', $txt['upgrade_step'], ' ', $step[0], ': ', $step[1], '</li>';
3351
3352
	echo '
3353
					</ul>
3354
			</div>
3355
3356
			<div id="progress_bar">
3357
				<div id="overall_text">', $upcontext['overall_percent'], '%</div>
3358
				<div id="overall_progress" style="width: ', $upcontext['overall_percent'], '%;">
3359
					<span>', $txt['upgrade_overall_progress'], '</span>
3360
				</div>
3361
			</div>';
3362
3363
	if (isset($upcontext['step_progress']))
3364
		echo '
3365
				<br>
3366
				<br>
3367
				<div id="progress_bar_step">
3368
					<div id="step_text">', $upcontext['step_progress'], '%</div>
3369
					<div id="step_progress" style="width: ', $upcontext['step_progress'], '%;background-color: #ffd000;">
3370
						<span>', $txt['upgrade_step_progress'], '</span>
3371
					</div>
3372
				</div>';
3373
3374
	echo '
3375
				<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>
3376
				<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', ';">
3377
					<div id="substep_text" style="color: #000; position: absolute; margin-left: -5em;">', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : '', '%</div>
3378
					<div id="substep_progress" style="width: ', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%; height: 12pt; z-index: 1; background-color: #eebaf4;">&nbsp;</div>
3379
				</div>';
3380
3381
	// How long have we been running this?
3382
	$elapsed = time() - $upcontext['started'];
3383
	$mins = (int) ($elapsed / 60);
3384
	$seconds = $elapsed - $mins * 60;
3385
	echo '
3386
								<br> <br> <br> <br> <br>
3387
								<div class="smalltext" style="padding: 5px; text-align: center;"><br>', $txt['upgrade_time_elapsed'], ':
3388
									<span id="mins_elapsed">', $mins, '</span> ', $txt['upgrade_time_mins'], ', <span id="secs_elapsed">', $seconds, '</span> ', $txt['upgrade_time_secs'], '.
3389
								</div>';
3390
	echo '
3391
			</div>
3392
			</div>
3393
			<div id="main_screen" class="clear">
3394
				<h2>', $upcontext['page_title'], '</h2>
3395
				<div class="panel">
3396
					<div style="max-height: 360px; overflow: auto;">';
3397
}
3398
3399
function template_upgrade_below()
3400
{
3401
	global $upcontext, $txt;
3402
3403
	if (!empty($upcontext['pause']))
3404
		echo '
3405
								<em>', $txt['upgrade_incomplete'], '.</em><br>
3406
3407
								<h2 style="margin-top: 2ex;">', $txt['upgrade_not_quite_done'], '</h2>
3408
								<h3>
3409
									', $txt['upgrade_paused_overload'], '
3410
								</h3>';
3411
3412
	if (!empty($upcontext['custom_warning']))
3413
		echo '
3414
								<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3415
									<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3416
									<strong style="text-decoration: underline;">', $txt['upgrade_note'], '</strong><br>
3417
									<div style="padding-left: 6ex;">', $upcontext['custom_warning'], '</div>
3418
								</div>';
3419
3420
	echo '
3421
								<div class="righttext" style="margin: 1ex;">';
3422
3423
	if (!empty($upcontext['continue']))
3424
		echo '
3425
									<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '"', $upcontext['continue'] == 2 ? ' disabled' : '', ' class="button_submit">';
3426
	if (!empty($upcontext['skip']))
3427
		echo '
3428
									<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="dontSubmit = true; document.getElementById(\'contbutt\').disabled = \'disabled\'; return true;" class="button_submit">';
3429
3430
	echo '
3431
								</div>
3432
							</form>
3433
						</div>
3434
				</div>
3435
			</div>
3436
			</div>
3437
		</div>
3438
		<div id="footer">
3439
			<ul>
3440
				<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>
3441
			</ul>
3442
		</div>
3443
	</body>
3444
</html>';
3445
3446
	// Are we on a pause?
3447
	if (!empty($upcontext['pause']))
3448
	{
3449
		echo '
3450
		<script>
3451
			window.onload = doAutoSubmit;
3452
			var countdown = 3;
3453
			var dontSubmit = false;
3454
3455
			function doAutoSubmit()
3456
			{
3457
				if (countdown == 0 && !dontSubmit)
3458
					document.upform.submit();
3459
				else if (countdown == -1)
3460
					return;
3461
3462
				document.getElementById(\'contbutt\').value = "', $txt['upgrade_continue'], ' (" + countdown + ")";
3463
				countdown--;
3464
3465
				setTimeout("doAutoSubmit();", 1000);
3466
			}
3467
		</script>';
3468
	}
3469
}
3470
3471
function template_xml_above()
3472
{
3473
	global $upcontext;
3474
3475
	echo '<', '?xml version="1.0" encoding="UTF-8"?', '>
3476
	<smf>';
3477
3478
	if (!empty($upcontext['get_data']))
3479
		foreach ($upcontext['get_data'] as $k => $v)
3480
			echo '
3481
		<get key="', $k, '">', $v, '</get>';
3482
}
3483
3484
function template_xml_below()
3485
{
3486
	echo '
3487
		</smf>';
3488
}
3489
3490
function template_error_message()
3491
{
3492
	global $upcontext;
3493
3494
	echo '
3495
	<div class="error_message red">
3496
		', $upcontext['error_msg'], '
3497
		<br>
3498
		<a href="', $_SERVER['PHP_SELF'], '">Click here to try again.</a>
3499
	</div>';
3500
}
3501
3502
function template_welcome_message()
0 ignored issues
show
Best Practice introduced by
The function template_welcome_message() has been defined more than once; this definition is ignored, only the first definition in other/install.php (L1905-1954) 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...
3503
{
3504
	global $upcontext, $disable_security, $settings, $txt;
3505
3506
	echo '
3507
		<script src="https://www.simplemachines.org/smf/current-version.js?version=' . SMF_VERSION . '"></script>
3508
			<h3>', sprintf($txt['upgrade_ready_proceed'], SMF_VERSION), '</h3>
3509
	<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">
3510
		<input type="hidden" name="', $upcontext['login_token_var'], '" value="', $upcontext['login_token'], '">
3511
		<div id="version_warning" style="margin: 2ex; padding: 2ex; border: 2px dashed #a92174; color: black; background-color: #fbbbe2; display: none;">
3512
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3513
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3514
			<div style="padding-left: 6ex;">
3515
				', sprintf($txt['upgrade_warning_out_of_date'], SMF_VERSION), '
3516
			</div>
3517
		</div>';
3518
3519
	$upcontext['chmod_in_form'] = true;
3520
	template_chmod();
3521
3522
	// For large, pre 1.1 RC2 forums give them a warning about the possible impact of this upgrade!
3523
	if ($upcontext['is_large_forum'])
3524
		echo '
3525
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3526
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3527
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3528
			<div style="padding-left: 6ex;">
3529
				', $txt['upgrade_warning_lots_data'], '
3530
			</div>
3531
		</div>';
3532
3533
	// A warning message?
3534
	if (!empty($upcontext['warning']))
3535
		echo '
3536
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3537
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3538
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3539
			<div style="padding-left: 6ex;">
3540
				', $upcontext['warning'], '
3541
			</div>
3542
		</div>';
3543
3544
	// Paths are incorrect?
3545
	echo '
3546
		<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">
3547
			<div style="float: left; width: 2ex; font-size: 2em; color: black;">!!</div>
3548
			<strong style="text-decoration: underline;">', $txt['upgrade_critical_error'], '</strong><br>
3549
			<div style="padding-left: 6ex;">
3550
				', $txt['upgrade_error_script_js'], '
3551
			</div>
3552
		</div>';
3553
3554
	// Is there someone already doing this?
3555
	if (!empty($upcontext['user']['id']) && (time() - $upcontext['started'] < 72600 || time() - $upcontext['updated'] < 3600))
3556
	{
3557
		$ago = time() - $upcontext['started'];
3558
		if ($ago < 60)
3559
			$ago = $ago . ' seconds';
3560
		elseif ($ago < 3600)
3561
			$ago = (int) ($ago / 60) . ' minutes';
3562
		else
3563
			$ago = (int) ($ago / 3600) . ' hours';
3564
3565
		$active = time() - $upcontext['updated'];
3566
		if ($active < 60)
3567
			$updated = $active . ' seconds';
3568
		elseif ($active < 3600)
3569
			$updated = (int) ($active / 60) . ' minutes';
3570
		else
3571
			$updated = (int) ($active / 3600) . ' hours';
3572
3573
		echo '
3574
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3575
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3576
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3577
			<div style="padding-left: 6ex;">
3578
				&quot;', $upcontext['user']['name'], '&quot; has been running the upgrade script for the last ', $ago, ' - and was last active ', $updated, ' ago.';
3579
3580
		if ($active < 600)
3581
			echo '
3582
				We recommend that you do not run this script unless you are sure that ', $upcontext['user']['name'], ' has completed their upgrade.';
3583
3584
		if ($active > $upcontext['inactive_timeout'])
3585
			echo '
3586
				<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.';
3587
		else
3588
			echo '
3589
				<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!');
3590
3591
		echo '
3592
			</div>
3593
		</div>';
3594
	}
3595
3596
	echo '
3597
			<strong>Admin Login: ', $disable_security ? '(DISABLED)' : '', '</strong>
3598
			<h3>For security purposes please login with your admin account to proceed with the upgrade.</h3>
3599
			<table>
3600
				<tr valign="top">
3601
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Username:</strong></td>
3602
					<td>
3603
						<input type="text" name="user" value="', !empty($upcontext['username']) ? $upcontext['username'] : '', '"', $disable_security ? ' disabled' : '', ' class="input_text">';
3604
3605
	if (!empty($upcontext['username_incorrect']))
3606
		echo '
3607
						<div class="smalltext" style="color: red;">Username Incorrect</div>';
3608
3609
	echo '
3610
					</td>
3611
				</tr>
3612
				<tr valign="top">
3613
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Password:</strong></td>
3614
					<td>
3615
						<input type="password" name="passwrd" value=""', $disable_security ? ' disabled' : '', ' class="input_password">
3616
						<input type="hidden" name="hash_passwrd" value="">';
3617
3618
	if (!empty($upcontext['password_failed']))
3619
		echo '
3620
						<div class="smalltext" style="color: red;">Password Incorrect</div>';
3621
3622
	echo '
3623
					</td>
3624
				</tr>';
3625
3626
	// Can they continue?
3627
	if (!empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] >= $upcontext['inactive_timeout'] && $upcontext['user']['step'] > 1)
3628
	{
3629
		echo '
3630
				<tr>
3631
					<td colspan="2">
3632
						<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>
3633
					</td>
3634
				</tr>';
3635
	}
3636
3637
	echo '
3638
			</table><br>
3639
			<span class="smalltext">
3640
				<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.
3641
			</span>
3642
			<input type="hidden" name="login_attempt" id="login_attempt" value="1">
3643
			<input type="hidden" name="js_works" id="js_works" value="0">';
3644
3645
	// Say we want the continue button!
3646
	$upcontext['continue'] = !empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] < $upcontext['inactive_timeout'] ? 2 : 1;
3647
3648
	// This defines whether javascript is going to work elsewhere :D
3649
	echo '
3650
		<script>
3651
			if (\'XMLHttpRequest\' in window && document.getElementById(\'js_works\'))
3652
				document.getElementById(\'js_works\').value = 1;
3653
3654
			// Latest version?
3655
			function smfCurrentVersion()
3656
			{
3657
				var smfVer, yourVer;
3658
3659
				if (!(\'smfVersion\' in window))
3660
					return;
3661
3662
				window.smfVersion = window.smfVersion.replace(/SMF\s?/g, \'\');
3663
3664
				smfVer = document.getElementById(\'smfVersion\');
3665
				yourVer = document.getElementById(\'yourVersion\');
3666
3667
				setInnerHTML(smfVer, window.smfVersion);
3668
3669
				var currentVersion = getInnerHTML(yourVer);
3670
				if (currentVersion < window.smfVersion)
3671
					document.getElementById(\'version_warning\').style.display = \'\';
3672
			}
3673
			addLoadEvent(smfCurrentVersion);
3674
3675
			// This checks that the script file even exists!
3676
			if (typeof(smfSelectText) == \'undefined\')
3677
				document.getElementById(\'js_script_missing_error\').style.display = \'\';
3678
3679
		</script>';
3680
}
3681
3682
function template_upgrade_options()
3683
{
3684
	global $upcontext, $modSettings, $db_prefix, $mmessage, $mtitle, $db_type;
3685
3686
	echo '
3687
			<h3>Before the upgrade gets underway please review the options below - and hit continue when you\'re ready to begin.</h3>
3688
			<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">';
3689
3690
	// Warning message?
3691
	if (!empty($upcontext['upgrade_options_warning']))
3692
		echo '
3693
		<div style="margin: 1ex; padding: 1ex; border: 1px dashed #cc3344; color: black; background-color: #ffe4e9;">
3694
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3695
			<strong style="text-decoration: underline;">Warning!</strong><br>
3696
			<div style="padding-left: 4ex;">
3697
				', $upcontext['upgrade_options_warning'], '
3698
			</div>
3699
		</div>';
3700
3701
	echo '
3702
				<table>
3703
					<tr valign="top">
3704
						<td width="2%">
3705
							<input type="checkbox" name="backup" id="backup" value="1" class="input_check">
3706
						</td>
3707
						<td width="100%">
3708
							<label for="backup">Backup tables in your database with the prefix &quot;backup_' . $db_prefix . '&quot;.</label> (recommended!)
3709
						</td>
3710
					</tr>
3711
					<tr valign="top">
3712
						<td width="2%">
3713
							<input type="checkbox" name="maint" id="maint" value="1" checked class="input_check">
3714
						</td>
3715
						<td width="100%">
3716
							<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>
3717
							<div id="mainmess" style="display: none;">
3718
								<strong class="smalltext">Maintenance Title: </strong><br>
3719
								<input type="text" name="maintitle" size="30" value="', htmlspecialchars($mtitle), '" class="input_text"><br>
3720
								<strong class="smalltext">Maintenance Message: </strong><br>
3721
								<textarea name="mainmessage" rows="3" cols="50">', htmlspecialchars($mmessage), '</textarea>
3722
							</div>
3723
						</td>
3724
					</tr>
3725
					<tr valign="top">
3726
						<td width="2%">
3727
							<input type="checkbox" name="debug" id="debug" value="1" class="input_check">
3728
						</td>
3729
						<td width="100%">
3730
							<label for="debug">Output extra debugging information</label>
3731
						</td>
3732
					</tr>
3733
					<tr valign="top">
3734
						<td width="2%">
3735
							<input type="checkbox" name="empty_error" id="empty_error" value="1" class="input_check">
3736
						</td>
3737
						<td width="100%">
3738
							<label for="empty_error">Empty error log before upgrading</label>
3739
						</td>
3740
					</tr>';
3741
3742
	if (!empty($upcontext['karma_installed']['good']) || !empty($upcontext['karma_installed']['bad']))
3743
		echo '
3744
					<tr valign="top">
3745
						<td width="2%">
3746
							<input type="checkbox" name="delete_karma" id="delete_karma" value="1" class="input_check">
3747
						</td>
3748
						<td width="100%">
3749
							<label for="delete_karma">Delete all karma settings and info from the DB</label>
3750
						</td>
3751
					</tr>';
3752
3753
	echo '
3754
					<tr valign="top">
3755
						<td width="2%">
3756
							<input type="checkbox" name="stat" id="stat" value="1"', empty($modSettings['allow_sm_stats']) ? '' : ' checked', ' class="input_check">
3757
						</td>
3758
						<td width="100%">
3759
							<label for="stat">
3760
								Allow Simple Machines to Collect Basic Stats Monthly.<br>
3761
								<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>
3762
							</label>
3763
						</td>
3764
					</tr>
3765
				</table>
3766
				<input type="hidden" name="upcont" value="1">';
3767
3768
	// We need a normal continue button here!
3769
	$upcontext['continue'] = 1;
3770
}
3771
3772
// Template for the database backup tool/
3773
function template_backup_database()
3774
{
3775
	global $upcontext, $support_js, $is_debug;
3776
3777
	echo '
3778
			<h3>Please wait while a backup is created. For large forums this may take some time!</h3>';
3779
3780
	echo '
3781
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
3782
			<input type="hidden" name="backup_done" id="backup_done" value="0">
3783
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
3784
			<div id="debug_section" style="height: ', ($is_debug ? '115' : '12') , 'px; overflow: auto;">
3785
			<span id="debuginfo"></span>
3786
			</div>';
3787
3788
	// Dont any tables so far?
3789
	if (!empty($upcontext['previous_tables']))
3790
		foreach ($upcontext['previous_tables'] as $table)
3791
			echo '
3792
			<br>Completed Table: &quot;', $table, '&quot;.';
3793
3794
	echo '
3795
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
3796
			<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>';
3797
3798
	// Continue please!
3799
	$upcontext['continue'] = $support_js ? 2 : 1;
3800
3801
	// If javascript allows we want to do this using XML.
3802
	if ($support_js)
3803
	{
3804
		echo '
3805
		<script>
3806
			var lastTable = ', $upcontext['cur_table_num'], ';
3807
			function getNextTables()
3808
			{
3809
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
3810
			}
3811
3812
			// Got an update!
3813
			function onBackupUpdate(oXMLDoc)
3814
			{
3815
				var sCurrentTableName = "";
3816
				var iTableNum = 0;
3817
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
3818
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
3819
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
3820
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
3821
3822
				// Update the page.
3823
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
3824
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
3825
				lastTable = iTableNum;
3826
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
3827
3828
		// If debug flood the screen.
3829
		if ($is_debug)
3830
			echo '
3831
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
3832
3833
				if (document.getElementById(\'debug_section\').scrollHeight)
3834
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
3835
3836
		echo '
3837
				// Get the next update...
3838
				if (iTableNum == ', $upcontext['table_count'], ')
3839
				{
3840
					document.getElementById(\'commess\').style.display = "";
3841
					document.getElementById(\'current_tab_div\').style.display = "none";
3842
					document.getElementById(\'contbutt\').disabled = 0;
3843
					document.getElementById(\'backup_done\').value = 1;
3844
				}
3845
				else
3846
					getNextTables();
3847
			}
3848
			getNextTables();
3849
		//# sourceURL=dynamicScript-bkup.js 
3850
		</script>';
3851
	}
3852
}
3853
3854
function template_backup_xml()
3855
{
3856
	global $upcontext;
3857
3858
	echo '
3859
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
3860
}
3861
3862
// Here is the actual "make the changes" template!
3863
function template_database_changes()
3864
{
3865
	global $upcontext, $support_js, $is_debug, $timeLimitThreshold;
3866
3867
	if (empty($is_debug) && !empty($upcontext['upgrade_status']['debug']))
3868
		$is_debug = true;
3869
3870
	echo '
3871
		<h3>Executing database changes</h3>
3872
		<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>';
3873
3874
	echo '
3875
		<form action="', $upcontext['form_url'], '&amp;filecount=', $upcontext['file_count'], '" name="upform" id="upform" method="post">
3876
		<input type="hidden" name="database_done" id="database_done" value="0">';
3877
3878
	// No javascript looks rubbish!
3879
	if (!$support_js)
3880
	{
3881
		foreach ($upcontext['actioned_items'] as $num => $item)
3882
		{
3883
			if ($num != 0)
3884
				echo ' Successful!';
3885
			echo '<br>' . $item;
3886
		}
3887 View Code Duplication
		if (!empty($upcontext['changes_complete']))
0 ignored issues
show
Duplication introduced by
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...
3888
		{
3889
			if ($is_debug)
3890
			{
3891
				$active = time() - $upcontext['started'];
3892
				$hours = floor($active / 3600);
3893
				$minutes = intval(($active / 60) % 60);
3894
				$seconds = intval($active % 60);
3895
3896
				$totalTime = '';
3897
				if ($hours > 0)
3898
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
3899
				if ($minutes > 0)
3900
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
3901
				if ($seconds > 0)
3902
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
3903
			}
3904
3905
			if ($is_debug && !empty($totalTime))
3906
				echo ' Successful! Completed in ', $totalTime, '<br><br>';
3907
			else
3908
				echo ' Successful!<br><br>';
3909
3910
			echo '<span id="commess" style="font-weight: bold;">1 Database Updates Complete! Click Continue to Proceed.</span><br>';
3911
		}
3912
	}
3913
	else
3914
	{
3915
		// Tell them how many files we have in total.
3916
		if ($upcontext['file_count'] > 1)
3917
			echo '
3918
		<strong id="info1">Executing upgrade script <span id="file_done">', $upcontext['cur_file_num'], '</span> of ', $upcontext['file_count'], '.</strong>';
3919
3920
		echo '
3921
		<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>
3922
		<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>';
3923
3924 View Code Duplication
		if ($is_debug)
0 ignored issues
show
Duplication introduced by
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...
3925
		{
3926
			if ($upcontext['current_debug_item_num'] == $upcontext['debug_items'])
3927
			{
3928
				$active = time() - $upcontext['started'];
3929
				$hours = floor($active / 3600);
3930
				$minutes = intval(($active / 60) % 60);
3931
				$seconds = intval($active % 60);
3932
3933
				$totalTime = '';
3934
				if ($hours > 0)
3935
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
3936
				if ($minutes > 0)
3937
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
3938
				if ($seconds > 0)
3939
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
3940
			}
3941
3942
			echo '
3943
			<br><span id="upgradeCompleted">';
3944
3945
			if (!empty($totalTime))
3946
				echo 'Completed in ', $totalTime, '<br>';
3947
3948
			echo '</span>
3949
			<div id="debug_section" style="height: 59px; overflow: auto;">
3950
			<span id="debuginfo"></span>
3951
			</div>';
3952
		}
3953
	}
3954
3955
	// Place for the XML error message.
3956
	echo '
3957
		<div id="error_block" style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9; display: ', empty($upcontext['error_message']) ? 'none' : '', ';">
3958
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3959
			<strong style="text-decoration: underline;">Error!</strong><br>
3960
			<div style="padding-left: 6ex;" id="error_message">', isset($upcontext['error_message']) ? $upcontext['error_message'] : 'Unknown Error!', '</div>
3961
		</div>';
3962
3963
	// We want to continue at some point!
3964
	$upcontext['continue'] = $support_js ? 2 : 1;
3965
3966
	// If javascript allows we want to do this using XML.
3967
	if ($support_js)
3968
	{
3969
		echo '
3970
		<script>
3971
			var lastItem = ', $upcontext['current_debug_item_num'], ';
3972
			var sLastString = "', strtr($upcontext['current_debug_item_name'], array('"' => '&quot;')), '";
3973
			var iLastSubStepProgress = -1;
3974
			var curFile = ', $upcontext['cur_file_num'], ';
3975
			var totalItems = 0;
3976
			var prevFile = 0;
3977
			var retryCount = 0;
3978
			var testvar = 0;
3979
			var timeOutID = 0;
3980
			var getData = "";
3981
			var debugItems = ', $upcontext['debug_items'], ';';
3982
3983
		if ($is_debug)
3984
			echo '
3985
			var upgradeStartTime = ' . $upcontext['started'] . ';';
3986
3987
		echo '
3988
			function getNextItem()
3989
			{
3990
				// We want to track this...
3991
				if (timeOutID)
3992
					clearTimeout(timeOutID);
3993
				timeOutID = window.setTimeout("retTimeout()", ', $timeLimitThreshold, '000);
3994
3995
				getXMLDocument(\'', $upcontext['form_url'], '&xml&filecount=', $upcontext['file_count'], '&substep=\' + lastItem + getData, onItemUpdate);
3996
			}
3997
3998
			// Got an update!
3999
			function onItemUpdate(oXMLDoc)
4000
			{
4001
				var sItemName = "";
4002
				var sDebugName = "";
4003
				var iItemNum = 0;
4004
				var iSubStepProgress = -1;
4005
				var iDebugNum = 0;
4006
				var bIsComplete = 0;
4007
				getData = "";
4008
4009
				// We\'ve got something - so reset the timeout!
4010
				if (timeOutID)
4011
					clearTimeout(timeOutID);
4012
4013
				// Assume no error at this time...
4014
				document.getElementById("error_block").style.display = "none";
4015
4016
				// Are we getting some duff info?
4017
				if (!oXMLDoc.getElementsByTagName("item")[0])
4018
				{
4019
					// Too many errors?
4020
					if (retryCount > 15)
4021
					{
4022
						document.getElementById("error_block").style.display = "";
4023
						setInnerHTML(document.getElementById("error_message"), "Error retrieving information on step: " + (sDebugName == "" ? sLastString : sDebugName));';
4024
4025
	if ($is_debug)
4026
		echo '
4027
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4028
4029
	echo '
4030
					}
4031
					else
4032
					{
4033
						retryCount++;
4034
						getNextItem();
4035
					}
4036
					return false;
4037
				}
4038
4039
				// Never allow loops.
4040
				if (curFile == prevFile)
4041
				{
4042
					retryCount++;
4043
					if (retryCount > 10)
4044
					{
4045
						document.getElementById("error_block").style.display = "";
4046
						setInnerHTML(document.getElementById("error_message"), "Upgrade script appears to be going into a loop - step: " + sDebugName);';
4047
4048
	if ($is_debug)
4049
		echo '
4050
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4051
4052
	echo '
4053
					}
4054
				}
4055
				retryCount = 0;
4056
4057
				for (var i = 0; i < oXMLDoc.getElementsByTagName("item")[0].childNodes.length; i++)
4058
					sItemName += oXMLDoc.getElementsByTagName("item")[0].childNodes[i].nodeValue;
4059
				for (var i = 0; i < oXMLDoc.getElementsByTagName("debug")[0].childNodes.length; i++)
4060
					sDebugName += oXMLDoc.getElementsByTagName("debug")[0].childNodes[i].nodeValue;
4061
				for (var i = 0; i < oXMLDoc.getElementsByTagName("get").length; i++)
4062
				{
4063
					getData += "&" + oXMLDoc.getElementsByTagName("get")[i].getAttribute("key") + "=";
4064
					for (var j = 0; j < oXMLDoc.getElementsByTagName("get")[i].childNodes.length; j++)
4065
					{
4066
						getData += oXMLDoc.getElementsByTagName("get")[i].childNodes[j].nodeValue;
4067
					}
4068
				}
4069
4070
				iItemNum = oXMLDoc.getElementsByTagName("item")[0].getAttribute("num");
4071
				iDebugNum = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("num"));
4072
				bIsComplete = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("complete"));
4073
				iSubStepProgress = parseFloat(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("percent"));
4074
				sLastString = sDebugName + " (Item: " + iDebugNum + ")";
4075
4076
				curFile = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("num"));
4077
				debugItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("debug_items"));
4078
				totalItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("items"));
4079
4080
				// If we have an error we haven\'t completed!
4081
				if (oXMLDoc.getElementsByTagName("error")[0] && bIsComplete)
4082
					iDebugNum = lastItem;
4083
4084
				// Do we have the additional progress bar?
4085
				if (iSubStepProgress != -1)
4086
				{
4087
					document.getElementById("substep_bar_div").style.display = "";
4088
					document.getElementById("substep_bar_div2").style.display = "";
4089
					document.getElementById("substep_progress").style.width = iSubStepProgress + "%";
4090
					setInnerHTML(document.getElementById("substep_text"), iSubStepProgress + "%");
4091
					setInnerHTML(document.getElementById("substep_bar_div"), sDebugName.replace(/\./g, "") + ":");
4092
				}
4093
				else
4094
				{
4095
					document.getElementById("substep_bar_div").style.display = "none";
4096
					document.getElementById("substep_bar_div2").style.display = "none";
4097
				}
4098
4099
				// Move onto the next item?
4100
				if (bIsComplete)
4101
					lastItem = iDebugNum;
4102
				else
4103
					lastItem = iDebugNum - 1;
4104
4105
				// Are we finished?
4106
				if (bIsComplete && iDebugNum == -1 && curFile >= ', $upcontext['file_count'], ')
4107
				{';
4108
4109
		if ($is_debug)
4110
			echo '
4111
					document.getElementById(\'debug_section\').style.display = "none";
4112
4113
					var upgradeFinishedTime = parseInt(oXMLDoc.getElementsByTagName("curtime")[0].childNodes[0].nodeValue);
4114
					var diffTime = upgradeFinishedTime - upgradeStartTime;
4115
					var diffHours = Math.floor(diffTime / 3600);
4116
					var diffMinutes = parseInt((diffTime / 60) % 60);
4117
					var diffSeconds = parseInt(diffTime % 60);
4118
4119
					var totalTime = "";
4120
					if (diffHours > 0)
4121
						totalTime = totalTime + diffHours + " hour" + (diffHours > 1 ? "s" : "") + " ";
4122
					if (diffMinutes > 0)
4123
						totalTime = totalTime + diffMinutes + " minute" + (diffMinutes > 1 ? "s" : "") + " ";
4124
					if (diffSeconds > 0)
4125
						totalTime = totalTime + diffSeconds + " second" + (diffSeconds > 1 ? "s" : "");
4126
4127
					setInnerHTML(document.getElementById("upgradeCompleted"), "Completed in " + totalTime);';
4128
4129
		echo '
4130
4131
					document.getElementById(\'commess\').style.display = "";
4132
					document.getElementById(\'contbutt\').disabled = 0;
4133
					document.getElementById(\'database_done\').value = 1;';
4134
4135
		if ($upcontext['file_count'] > 1)
4136
			echo '
4137
					document.getElementById(\'info1\').style.display = "none";';
4138
4139
		echo '
4140
					document.getElementById(\'info2\').style.display = "none";
4141
					updateStepProgress(100, 100, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4142
					return true;
4143
				}
4144
				// Was it the last step in the file?
4145
				else if (bIsComplete && iDebugNum == -1)
4146
				{
4147
					lastItem = 0;
4148
					prevFile = curFile;';
4149
4150
		if ($is_debug)
4151
			echo '
4152
					setOuterHTML(document.getElementById(\'debuginfo\'), \'Moving to next script file...done<br><span id="debuginfo"><\' + \'/span>\');';
4153
4154
		echo '
4155
					getNextItem();
4156
					return true;
4157
				}';
4158
4159
		// If debug scroll the screen.
4160
		if ($is_debug)
4161
			echo '
4162
				if (iLastSubStepProgress == -1)
4163
				{
4164
					// Give it consistent dots.
4165
					dots = sDebugName.match(/\./g);
4166
					numDots = dots ? dots.length : 0;
4167
					for (var i = numDots; i < 3; i++)
4168
						sDebugName += ".";
4169
					setOuterHTML(document.getElementById(\'debuginfo\'), sDebugName + \'<span id="debuginfo"><\' + \'/span>\');
4170
				}
4171
				iLastSubStepProgress = iSubStepProgress;
4172
4173
				if (bIsComplete)
4174
					setOuterHTML(document.getElementById(\'debuginfo\'), \'done<br><span id="debuginfo"><\' + \'/span>\');
4175
				else
4176
					setOuterHTML(document.getElementById(\'debuginfo\'), \'...<span id="debuginfo"><\' + \'/span>\');
4177
4178
				if (document.getElementById(\'debug_section\').scrollHeight)
4179
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4180
4181
		echo '
4182
				// Update the page.
4183
				setInnerHTML(document.getElementById(\'item_num\'), iItemNum);
4184
				setInnerHTML(document.getElementById(\'cur_item_name\'), sItemName);';
4185
4186
		if ($upcontext['file_count'] > 1)
4187
		{
4188
			echo '
4189
				setInnerHTML(document.getElementById(\'file_done\'), curFile);
4190
				setInnerHTML(document.getElementById(\'item_count\'), totalItems);';
4191
		}
4192
4193
		echo '
4194
				// Is there an error?
4195
				if (oXMLDoc.getElementsByTagName("error")[0])
4196
				{
4197
					var sErrorMsg = "";
4198
					for (var i = 0; i < oXMLDoc.getElementsByTagName("error")[0].childNodes.length; i++)
4199
						sErrorMsg += oXMLDoc.getElementsByTagName("error")[0].childNodes[i].nodeValue;
4200
					document.getElementById("error_block").style.display = "";
4201
					setInnerHTML(document.getElementById("error_message"), sErrorMsg);
4202
					return false;
4203
				}
4204
4205
				// Get the progress bar right.
4206
				barTotal = debugItems * ', $upcontext['file_count'], ';
4207
				barDone = (debugItems * (curFile - 1)) + lastItem;
4208
4209
				updateStepProgress(barDone, barTotal, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4210
4211
				// Finally - update the time here as it shows the server is responding!
4212
				curTime = new Date();
4213
				iElapsed = (curTime.getTime() / 1000 - ', $upcontext['started'], ');
4214
				mins = parseInt(iElapsed / 60);
4215
				secs = parseInt(iElapsed - mins * 60);
4216
				setInnerHTML(document.getElementById("mins_elapsed"), mins);
4217
				setInnerHTML(document.getElementById("secs_elapsed"), secs);
4218
4219
				getNextItem();
4220
				return true;
4221
			}
4222
4223
			// What if we timeout?!
4224
			function retTimeout(attemptAgain)
4225
			{
4226
				// Oh noes...
4227
				if (!attemptAgain)
4228
				{
4229
					document.getElementById("error_block").style.display = "";
4230
					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");
4231
				}
4232
				else
4233
				{
4234
					document.getElementById("error_block").style.display = "none";
4235
					getNextItem();
4236
				}
4237
			}';
4238
4239
		// Start things off assuming we've not errored.
4240
		if (empty($upcontext['error_message']))
4241
			echo '
4242
			getNextItem();';
4243
4244
		echo '
4245
		//# sourceURL=dynamicScript-dbch.js 
4246
		</script>';
4247
	}
4248
	return;
4249
}
4250
4251
function template_database_xml()
4252
{
4253
	global $is_debug, $upcontext;
4254
4255
	echo '
4256
	<file num="', $upcontext['cur_file_num'], '" items="', $upcontext['total_items'], '" debug_items="', $upcontext['debug_items'], '">', $upcontext['cur_file_name'], '</file>
4257
	<item num="', $upcontext['current_item_num'], '">', $upcontext['current_item_name'], '</item>
4258
	<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>';
4259
4260
	if (!empty($upcontext['error_message']))
4261
		echo '
4262
	<error>', $upcontext['error_message'], '</error>';
4263
4264
	if (!empty($upcontext['error_string']))
4265
		echo '
4266
	<sql>', $upcontext['error_string'], '</sql>';
4267
4268
	if ($is_debug)
4269
		echo '
4270
	<curtime>', time(), '</curtime>';
4271
}
4272
4273
// Template for the UTF-8 conversion step. Basically a copy of the backup stuff with slight modifications....
4274 View Code Duplication
function template_convert_utf8()
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in 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...
4275
{
4276
	global $upcontext, $support_js, $is_debug;
4277
4278
	echo '
4279
			<h3>Please wait while your database is converted to UTF-8. For large forums this may take some time!</h3>';
4280
4281
	echo '
4282
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4283
			<input type="hidden" name="utf8_done" id="utf8_done" value="0">
4284
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
4285
			<div id="debug_section" style="height: ', ($is_debug ? '115' : '12') , 'px; overflow: auto;">
4286
			<span id="debuginfo"></span>
4287
			</div>';
4288
4289
	// Done any tables so far?
4290
	if (!empty($upcontext['previous_tables']))
4291
		foreach ($upcontext['previous_tables'] as $table)
4292
			echo '
4293
			<br>Completed Table: &quot;', $table, '&quot;.';
4294
4295
	echo '
4296
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>';
4297
4298
	// If we dropped their index, let's let them know
4299
	if ($upcontext['cur_table_num'] == $upcontext['table_count'] && $upcontext['dropping_index'])
4300
		echo '
4301
			<br><span style="display:inline;">Please note that your fulltext index was dropped to facilitate the conversion and will need to be recreated.</span>';
4302
4303
	echo '
4304
			<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>';
4305
4306
	// Continue please!
4307
	$upcontext['continue'] = $support_js ? 2 : 1;
4308
4309
	// If javascript allows we want to do this using XML.
4310
	if ($support_js)
4311
	{
4312
		echo '
4313
		<script>
4314
			var lastTable = ', $upcontext['cur_table_num'], ';
4315
			function getNextTables()
4316
			{
4317
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onConversionUpdate);
4318
			}
4319
4320
			// Got an update!
4321
			function onConversionUpdate(oXMLDoc)
4322
			{
4323
				var sCurrentTableName = "";
4324
				var iTableNum = 0;
4325
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4326
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4327
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4328
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4329
4330
				// Update the page.
4331
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4332
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4333
				lastTable = iTableNum;
4334
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4335
4336
		// If debug flood the screen.
4337
		if ($is_debug)
4338
			echo '
4339
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4340
4341
				if (document.getElementById(\'debug_section\').scrollHeight)
4342
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4343
4344
		echo '
4345
				// Get the next update...
4346
				if (iTableNum == ', $upcontext['table_count'], ')
4347
				{
4348
					document.getElementById(\'commess\').style.display = "";
4349
					document.getElementById(\'current_tab_div\').style.display = "none";
4350
					document.getElementById(\'contbutt\').disabled = 0;
4351
					document.getElementById(\'utf8_done\').value = 1;
4352
				}
4353
				else
4354
					getNextTables();
4355
			}
4356
			getNextTables();
4357
		//# sourceURL=dynamicScript-conv.js 
4358
		</script>';
4359
	}
4360
}
4361
4362
function template_convert_xml()
4363
{
4364
	global $upcontext;
4365
4366
	echo '
4367
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4368
}
4369
4370
// Template for the database backup tool/
4371 View Code Duplication
function template_serialize_json()
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in 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...
4372
{
4373
	global $upcontext, $support_js, $is_debug;
4374
4375
	echo '
4376
			<h3>Converting data from serialize to JSON...</h3>';
4377
4378
	echo '
4379
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4380
			<input type="hidden" name="json_done" id="json_done" value="0">
4381
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
4382
			<div id="debug_section" style="height: ', ($is_debug ? '115' : '12') , 'px; overflow: auto;">
4383
			<span id="debuginfo"></span>
4384
			</div>';
4385
4386
	// Dont any tables so far?
4387
	if (!empty($upcontext['previous_tables']))
4388
		foreach ($upcontext['previous_tables'] as $table)
4389
			echo '
4390
			<br>Completed Table: &quot;', $table, '&quot;.';
4391
4392
	echo '
4393
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
4394
			<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>';
4395
4396
	// Try to make sure substep was reset.
4397
	if ($upcontext['cur_table_num'] == $upcontext['table_count'])
4398
		echo '
4399
			<input type="hidden" name="substep" id="substep" value="0">';
4400
4401
	// Continue please!
4402
	$upcontext['continue'] = $support_js ? 2 : 1;
4403
4404
	// If javascript allows we want to do this using XML.
4405
	if ($support_js)
4406
	{
4407
		echo '
4408
		<script>
4409
			var lastTable = ', $upcontext['cur_table_num'], ';
4410
			function getNextTables()
4411
			{
4412
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4413
			}
4414
4415
			// Got an update!
4416
			function onBackupUpdate(oXMLDoc)
4417
			{
4418
				var sCurrentTableName = "";
4419
				var iTableNum = 0;
4420
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4421
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4422
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4423
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4424
4425
				// Update the page.
4426
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4427
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4428
				lastTable = iTableNum;
4429
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4430
4431
		// If debug flood the screen.
4432
		if ($is_debug)
4433
			echo '
4434
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4435
4436
				if (document.getElementById(\'debug_section\').scrollHeight)
4437
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4438
4439
		echo '
4440
				// Get the next update...
4441
				if (iTableNum == ', $upcontext['table_count'], ')
4442
				{
4443
					document.getElementById(\'commess\').style.display = "";
4444
					document.getElementById(\'current_tab_div\').style.display = "none";
4445
					document.getElementById(\'contbutt\').disabled = 0;
4446
					document.getElementById(\'json_done\').value = 1;
4447
				}
4448
				else
4449
					getNextTables();
4450
			}
4451
			getNextTables();
4452
		//# sourceURL=dynamicScript-json.js 
4453
		</script>';
4454
	}
4455
}
4456
4457
function template_serialize_json_xml()
4458
{
4459
	global $upcontext;
4460
4461
	echo '
4462
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4463
}
4464
4465
function template_upgrade_complete()
4466
{
4467
	global $upcontext, $upgradeurl, $settings, $boardurl, $is_debug;
4468
4469
	echo '
4470
	<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>
4471
	<form action="', $boardurl, '/index.php">';
4472
4473
	if (!empty($upcontext['can_delete_script']))
4474
		echo '
4475
			<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>
4476
			<script>
4477
				function doTheDelete(theCheck)
4478
				{
4479
					var theImage = document.getElementById ? document.getElementById("delete_upgrader") : document.all.delete_upgrader;
4480
4481
					theImage.src = "', $upgradeurl, '?delete=1&ts_" + (new Date().getTime());
4482
					theCheck.disabled = true;
4483
				}
4484
			</script>
4485
			<img src="', $settings['default_theme_url'], '/images/blank.png" alt="" id="delete_upgrader"><br>';
4486
4487
	$active = time() - $upcontext['started'];
4488
	$hours = floor($active / 3600);
4489
	$minutes = intval(($active / 60) % 60);
4490
	$seconds = intval($active % 60);
4491
4492
	if ($is_debug)
4493
	{
4494
		$totalTime = '';
4495
		if ($hours > 0)
4496
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
4497
		if ($minutes > 0)
4498
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
4499
		if ($seconds > 0)
4500
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
4501
	}
4502
4503
	if ($is_debug && !empty($totalTime))
4504
		echo '<br> Upgrade completed in ', $totalTime, '<br><br>';
4505
4506
	echo '<br>
4507
			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>
4508
			<br>
4509
			Best of luck,<br>
4510
			Simple Machines';
4511
}
4512
4513
/**
4514
 * Convert MySQL (var)char ip col to binary
4515
 *
4516
 * @param string $targetTable The table to perform the operation on
4517
 * @param string $oldCol The old column to gather data from
4518
 * @param string $newCol The new column to put data in
4519
 * @param int $limit The amount of entries to handle at once.
4520
 * @param int $setSize The amount of entries after which to update the database.
4521
 *
4522
 * newCol needs to be a varbinary(16) null able field
4523
 * @return bool
4524
 */
4525
function MySQLConvertOldIp($targetTable, $oldCol, $newCol, $limit = 50000, $setSize = 100)
4526
{
4527
	global $smcFunc, $step_progress;
4528
4529
	$current_substep = $_GET['substep'];
4530
4531
	if (empty($_GET['a']))
4532
		$_GET['a'] = 0;
4533
	$step_progress['name'] = 'Converting ips';
4534
	$step_progress['current'] = $_GET['a'];
4535
4536
	// Skip this if we don't have the column
4537
	$request = $smcFunc['db_query']('', '
4538
		SHOW FIELDS
4539
		FROM {db_prefix}{raw:table}
4540
		WHERE Field = {string:name}',
4541
		array(
4542
			'table' => $targetTable,
4543
			'name' => $oldCol,
4544
	));
4545
	if ($smcFunc['db_num_rows']($request) !== 1)
4546
	{
4547
		$smcFunc['db_free_result']($request);
4548
		return;
4549
	}
4550
	$smcFunc['db_free_result']($request);
4551
4552
	//mysql default max length is 1mb https://dev.mysql.com/doc/refman/5.1/en/packet-too-large.html
4553
	$arIp = array();
0 ignored issues
show
Unused Code introduced by
$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...
4554
4555
	$is_done = false;
4556
	while (!$is_done)
4557
	{
4558
		// Keep looping at the current step.
4559
		nextSubStep($current_substep);
4560
4561
		$arIp = array();
4562
4563
		$request = $smcFunc['db_query']('', '
4564
			SELECT DISTINCT {raw:old_col}
4565
			FROM {db_prefix}{raw:table_name}
4566
			WHERE {raw:new_col} IS NULL
4567
			LIMIT {int:limit}',
4568
			array(
4569
				'old_col' => $oldCol,
4570
				'new_col' => $newCol,
4571
				'table_name' => $targetTable,
4572
				'empty' => '',
4573
				'limit' => $limit,
4574
		));
4575
		while ($row = $smcFunc['db_fetch_assoc']($request))
4576
			$arIp[] = $row[$oldCol];
4577
		$smcFunc['db_free_result']($request);
4578
4579
		// Special case, null ip could keep us in a loop.
4580
		if (is_null($arIp[0]))
4581
			unset($arIp[0]);
4582
4583
		if (empty($arIp))
4584
			$is_done = true;
4585
4586
		$updates = array();
4587
		$cases = array();
4588
		$count = count($arIp);
4589
		for ($i = 0; $i < $count; $i++)
4590
		{
4591
			$arIp[$i] = trim($arIp[$i]);
4592
4593
			if (empty($arIp[$i]))
4594
				continue;
4595
4596
			$updates['ip' . $i] = $arIp[$i];
4597
			$cases[$arIp[$i]] = 'WHEN ' . $oldCol . ' = {string:ip' . $i . '} THEN {inet:ip' . $i . '}';
4598
4599
			if ($setSize > 0 && $i % $setSize === 0)
4600
			{
4601
				if (count($updates) == 1)
4602
					continue;
4603
4604
				$updates['whereSet'] = array_values($updates);
4605
				$smcFunc['db_query']('', '
4606
					UPDATE {db_prefix}' . $targetTable . '
4607
					SET ' . $newCol . ' = CASE ' .
4608
					implode('
4609
						', $cases) . '
4610
						ELSE NULL
4611
					END
4612
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4613
					$updates
4614
				);
4615
4616
				$updates = array();
4617
				$cases = array();
4618
			}
4619
		}
4620
4621
		// Incase some extras made it through.
4622
		if (!empty($updates))
4623
		{
4624
			if (count($updates) == 1)
4625
			{
4626
				foreach ($updates as $key => $ip)
4627
				{
4628
					$smcFunc['db_query']('', '
4629
						UPDATE {db_prefix}' . $targetTable . '
4630
						SET ' . $newCol . ' = {inet:ip}
4631
						WHERE ' . $oldCol . ' = {string:ip}',
4632
						array(
4633
							'ip' => $ip
4634
					));
4635
				}
4636
			}
4637
			else
4638
			{
4639
				$updates['whereSet'] = array_values($updates);
4640
				$smcFunc['db_query']('', '
4641
					UPDATE {db_prefix}' . $targetTable . '
4642
					SET ' . $newCol . ' = CASE ' .
4643
					implode('
4644
						', $cases) . '
4645
						ELSE NULL
4646
					END
4647
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4648
					$updates
4649
				);
4650
			}
4651
		}
4652
		else
4653
			$is_done = true;
4654
4655
		$_GET['a'] += $limit;
4656
		$step_progress['current'] = $_GET['a'];
4657
	}
4658
4659
	unset($_GET['a']);
4660
}
4661
4662
/**
4663
 * Get the column info. This is basically the same as smf_db_list_columns but we get 1 column, force detail and other checks.
4664
 *
4665
 * @param string $targetTable The table to perform the operation on
4666
 * @param string $column The column we are looking for.
4667
 *
4668
 * @return array Info on the table.
4669
 */
4670
function upgradeGetColumnInfo($targetTable, $column)
4671
{
4672
 	global $smcFunc;
4673
4674
 	// This should already be here, but be safe.
4675
 	db_extend('packages');
4676
 
4677
 	$columns = $smcFunc['db_list_columns']($targetTable, true);
4678
4679
	if (isset($columns[$column]))
4680
		return $columns[$column];
4681
	else
4682
		return null;
4683
}
4684
4685
?>
0 ignored issues
show
Best Practice introduced by
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...