Failed Conditions
Branch release-2.1 (4e22cf)
by Rick
06:39
created

upgrade.php ➔ checkChange()   C

Complexity

Conditions 11
Paths 33

Size

Total Lines 69
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 32
nc 33
nop 1
dl 0
loc 69
rs 5.7847
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines http://www.simplemachines.org
8
 * @copyright 2017 Simple Machines and individual contributors
9
 * @license http://www.simplemachines.org/about/smf/license.php BSD
10
 *
11
 * @version 2.1 Beta 4
12
 */
13
14
// Version information...
15
define('SMF_VERSION', '2.1 Beta 4');
16
define('SMF_LANG_VERSION', '2.1 Beta 4');
17
18
/**
19
 * The minimum required PHP version.
20
 * @var string
21
 */
22
$GLOBALS['required_php_version'] = '5.4.0';
23
24
/**
25
 * A list of supported database systems.
26
 * @var array
27
 */
28
$databases = array(
29
	'mysql' => array(
30
		'name' => 'MySQL',
31
		'version' => '5.0.3',
32
		'version_check' => 'global $db_connection; return min(mysqli_get_server_info($db_connection), mysqli_get_client_info());',
33
		'utf8_support' => true,
34
		'utf8_version' => '5.0.3',
35
		'utf8_version_check' => 'global $db_connection; return mysqli_get_server_info($db_connection);',
36
		'alter_support' => true,
37
	),
38
	'postgresql' => array(
39
		'name' => 'PostgreSQL',
40
		'version' => '9.1',
41
		'version_check' => '$version = pg_version(); return $version[\'client\'];',
42
		'always_has_db' => true,
43
	),
44
);
45
46
/**
47
 * The maximum time a single substep may take, in seconds.
48
 * @var int
49
 */
50
$timeLimitThreshold = 30;
51
52
/**
53
 * The current path to the upgrade.php file.
54
 * @var string
55
 */
56
$upgrade_path = dirname(__FILE__);
57
58
/**
59
 * The URL of the current page.
60
 * @var string
61
 */
62
$upgradeurl = $_SERVER['PHP_SELF'];
63
64
/**
65
 * Flag to disable the required administrator login.
66
 * @var bool
67
 */
68
$disable_security = false;
69
70
/**
71
 * The amount of seconds allowed between logins.
72
 * If the first user to login is inactive for this amount of seconds, a second login is allowed.
73
 * @var int
74
 */
75
$upcontext['inactive_timeout'] = 10;
76
77
// The helper is crucial. Include it first thing.
78
if (!file_exists($upgrade_path . '/upgrade-helper.php'))
79
    die('upgrade-helper.php not found where it was expected: ' . $upgrade_path . '/upgrade-helper.php! Make sure you have uploaded ALL files from the upgrade package. The upgrader cannot continue.');
80
81
require_once($upgrade_path . '/upgrade-helper.php');
82
83
// All the steps in detail.
84
// Number,Name,Function,Progress Weight.
85
$upcontext['steps'] = array(
86
	0 => array(1, 'Login', 'WelcomeLogin', 2),
87
	1 => array(2, 'Upgrade Options', 'UpgradeOptions', 2),
88
	2 => array(3, 'Backup', 'BackupDatabase', 10),
89
	3 => array(4, 'Database Changes', 'DatabaseChanges', 50),
90
	4 => array(5, 'Convert to UTF-8', 'ConvertUtf8', 20),
91
	5 => array(6, 'Convert serialized strings to JSON', 'serialize_to_json', 10),
92
	6 => array(7, 'Delete Upgrade.php', 'DeleteUpgrade', 1),
93
);
94
// Just to remember which one has files in it.
95
$upcontext['database_step'] = 3;
96
@set_time_limit(600);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
97
if (!ini_get('safe_mode'))
98
{
99
	ini_set('mysql.connect_timeout', -1);
100
	ini_set('default_socket_timeout', 900);
101
}
102
// Clean the upgrade path if this is from the client.
103
if (!empty($_SERVER['argv']) && php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
104
	for ($i = 1; $i < $_SERVER['argc']; $i++)
105
	{
106
		if (preg_match('~^--path=(.+)$~', $_SERVER['argv'][$i], $match) != 0)
107
			$upgrade_path = substr($match[1], -1) == '/' ? substr($match[1], 0, -1) : $match[1];
108
	}
109
110
// Are we from the client?
111
if (php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
112
{
113
	$command_line = true;
114
	$disable_security = true;
115
}
116
else
117
	$command_line = false;
118
119
// Load this now just because we can.
120
require_once($upgrade_path . '/Settings.php');
121
122
// We don't use "-utf8" anymore...  Tweak the entry that may have been loaded by Settings.php
123
if (isset($language))
124
	$language = str_ireplace('-utf8', '', $language);
125
126
// Are we logged in?
127
if (isset($upgradeData))
128
{
129
	$upcontext['user'] = json_decode(base64_decode($upgradeData), true);
130
131
	// Check for sensible values.
132 View Code Duplication
	if (empty($upcontext['user']['started']) || $upcontext['user']['started'] < time() - 86400)
133
		$upcontext['user']['started'] = time();
134 View Code Duplication
	if (empty($upcontext['user']['updated']) || $upcontext['user']['updated'] < time() - 86400)
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
	// SMF isn't started up properly, but loadUserSettings calls our cookies.
170 View Code Duplication
	if (!isset($smcFunc['json_encode']))
171
	{
172
		$smcFunc['json_encode'] = 'json_encode';
173
		$smcFunc['json_decode'] = 'smf_json_decode';
174
	}
175
176
	loadUserSettings();
177
	loadPermissions();
178
}
179
180
// Include our helper functions.
181
require_once($sourcedir . '/Subs.php');
182
require_once($sourcedir . '/LogInOut.php');
183
184
// This only exists if we're on SMF ;)
185
if (isset($modSettings['smfVersion']))
186
{
187
	$request = $smcFunc['db_query']('', '
188
		SELECT variable, value
189
		FROM {db_prefix}themes
190
		WHERE id_theme = {int:id_theme}
191
			AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
192
		array(
193
			'id_theme' => 1,
194
			'theme_url' => 'theme_url',
195
			'theme_dir' => 'theme_dir',
196
			'images_url' => 'images_url',
197
			'db_error_skip' => true,
198
		)
199
	);
200 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
201
		$modSettings[$row['variable']] = $row['value'];
202
	$smcFunc['db_free_result']($request);
203
}
204
205
if (!isset($modSettings['theme_url']))
206
{
207
	$modSettings['theme_dir'] = $boarddir . '/Themes/default';
208
	$modSettings['theme_url'] = 'Themes/default';
209
	$modSettings['images_url'] = 'Themes/default/images';
210
}
211
if (!isset($settings['default_theme_url']))
212
	$settings['default_theme_url'] = $modSettings['theme_url'];
213
if (!isset($settings['default_theme_dir']))
214
	$settings['default_theme_dir'] = $modSettings['theme_dir'];
215
216
$upcontext['is_large_forum'] = (empty($modSettings['smfVersion']) || $modSettings['smfVersion'] <= '1.1 RC1') && !empty($modSettings['totalMessages']) && $modSettings['totalMessages'] > 75000;
217
// Default title...
218
$upcontext['page_title'] = 'Updating Your SMF Installation!';
219
220
// Have we got tracking data - if so use it (It will be clean!)
221
if (isset($_GET['data']))
222
{
223
	global $is_debug;
224
225
	$upcontext['upgrade_status'] = json_decode(base64_decode($_GET['data']), true);
226
	$upcontext['current_step'] = $upcontext['upgrade_status']['curstep'];
227
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
228
	$upcontext['rid'] = $upcontext['upgrade_status']['rid'];
229
	$support_js = $upcontext['upgrade_status']['js'];
230
231
	// Only set this if the upgrader status says so.
232
	if (empty($is_debug))
233
		$is_debug = $upcontext['upgrade_status']['debug'];
234
235
	// Load the language.
236 View Code Duplication
	if (file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
237
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
238
}
239
// Set the defaults.
240
else
241
{
242
	$upcontext['current_step'] = 0;
243
	$upcontext['rid'] = mt_rand(0, 5000);
244
	$upcontext['upgrade_status'] = array(
245
		'curstep' => 0,
246
		'lang' => isset($_GET['lang']) ? $_GET['lang'] : basename($language, '.lng'),
247
		'rid' => $upcontext['rid'],
248
		'pass' => 0,
249
		'debug' => 0,
250
		'js' => 0,
251
	);
252
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
253
}
254
255
// If this isn't the first stage see whether they are logging in and resuming.
256
if ($upcontext['current_step'] != 0 || !empty($upcontext['user']['step']))
257
	checkLogin();
258
259
if ($command_line)
260
	cmdStep0();
261
262
// Don't error if we're using xml.
263
if (isset($_GET['xml']))
264
	$upcontext['return_error'] = true;
265
266
// Loop through all the steps doing each one as required.
267
$upcontext['overall_percent'] = 0;
268
foreach ($upcontext['steps'] as $num => $step)
269
{
270
	if ($num >= $upcontext['current_step'])
271
	{
272
		// The current weight of this step in terms of overall progress.
273
		$upcontext['step_weight'] = $step[3];
274
		// Make sure we reset the skip button.
275
		$upcontext['skip'] = false;
276
277
		// We cannot proceed if we're not logged in.
278
		if ($num != 0 && !$disable_security && $upcontext['user']['pass'] != $upcontext['upgrade_status']['pass'])
279
		{
280
			$upcontext['steps'][0][2]();
281
			break;
282
		}
283
284
		// Call the step and if it returns false that means pause!
285
		if (function_exists($step[2]) && $step[2]() === false)
286
			break;
287
		elseif (function_exists($step[2])) {
288
			//Start each new step with this unset, so the 'normal' template is called first
289
			unset($_GET['xml']);
290
			//Clear out warnings at the start of each step
291
			unset($upcontext['custom_warning']);
292
			$_GET['substep'] = 0;
293
			$upcontext['current_step']++;
294
		}
295
	}
296
	$upcontext['overall_percent'] += $step[3];
297
}
298
299
upgradeExit();
300
301
// Exit the upgrade script.
302
function upgradeExit($fallThrough = false)
303
{
304
	global $upcontext, $upgradeurl, $sourcedir, $command_line, $is_debug;
305
306
	// Save where we are...
307
	if (!empty($upcontext['current_step']) && !empty($upcontext['user']['id']))
308
	{
309
		$upcontext['user']['step'] = $upcontext['current_step'];
310
		$upcontext['user']['substep'] = $_GET['substep'];
311
		$upcontext['user']['updated'] = time();
312
		$upcontext['debug'] = $is_debug;
313
		$upgradeData = base64_encode(json_encode($upcontext['user']));
314
		require_once($sourcedir . '/Subs-Admin.php');
315
		updateSettingsFile(array('upgradeData' => '"' . $upgradeData . '"'));
316
		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...
317
	}
318
319
	// Handle the progress of the step, if any.
320
	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
321
	{
322
		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
323
		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
324
	}
325
	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
326
327
	// We usually dump our templates out.
328
	if (!$fallThrough)
329
	{
330
		// This should not happen my dear... HELP ME DEVELOPERS!!
331
		if (!empty($command_line))
332
		{
333
			if (function_exists('debug_print_backtrace'))
334
				debug_print_backtrace();
335
336
			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.';
337
			flush();
338
			die();
339
		}
340
341
		if (!isset($_GET['xml']))
342
			template_upgrade_above();
343
		else
344
		{
345
			header('Content-Type: text/xml; charset=UTF-8');
346
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
347
			$upcontext['get_data'] = array();
348
			foreach ($_GET as $k => $v)
349
			{
350
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
351
				{
352
					$upcontext['get_data'][$k] = $v;
353
				}
354
			}
355
			template_xml_above();
356
		}
357
358
		// Call the template.
359
		if (isset($upcontext['sub_template']))
360
		{
361
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
362
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(json_encode($upcontext['upgrade_status']));
363
364
			// Custom stuff to pass back?
365
			if (!empty($upcontext['query_string']))
366
				$upcontext['form_url'] .= $upcontext['query_string'];
367
368
			// Call the appropriate subtemplate
369
			if (is_callable('template_' . $upcontext['sub_template']))
370
				call_user_func('template_' . $upcontext['sub_template']);
371
			else
372
				die('Upgrade aborted!  Invalid template: template_' . $upcontext['sub_template']);
373
		}
374
375
		// Was there an error?
376
		if (!empty($upcontext['forced_error_message']))
377
			echo $upcontext['forced_error_message'];
378
379
		// Show the footer.
380
		if (!isset($_GET['xml']))
381
			template_upgrade_below();
382
		else
383
			template_xml_below();
384
	}
385
386
387
	if (!empty($command_line) && $is_debug)
388
	{
389
		$active = time() - $upcontext['started'];
390
		$hours = floor($active / 3600);
391
		$minutes = intval(($active / 60) % 60);
392
		$seconds = intval($active % 60);
393
394
		$totalTime = '';
395
		if ($hours > 0)
396
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
397
		if ($minutes > 0)
398
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
399
		if ($seconds > 0)
400
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
401
402
		if (!empty($totalTime))
403
			echo "\n" . 'Upgrade completed in ' . $totalTime . "\n";
404
	}
405
406
	// Bang - gone!
407
	die();
408
}
409
410
// Used to direct the user to another location.
411
function redirectLocation($location, $addForm = true)
412
{
413
	global $upgradeurl, $upcontext, $command_line;
414
415
	// Command line users can't be redirected.
416
	if ($command_line)
417
		upgradeExit(true);
418
419
	// Are we providing the core info?
420
	if ($addForm)
421
	{
422
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
423
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
424
	}
425
426
	while (@ob_end_clean());
427
	header('Location: ' . strtr($location, array('&amp;' => '&')));
428
429
	// Exit - saving status as we go.
430
	upgradeExit(true);
431
}
432
433
// Load all essential data and connect to the DB as this is pre SSI.php
434
function loadEssentialData()
435
{
436
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection, $db_prefix, $db_character_set, $db_type;
437
	global $modSettings, $sourcedir, $smcFunc;
438
439
	error_reporting(E_ALL);
440
	define('SMF', 1);
441
442
	// Start the session.
443
	if (@ini_get('session.save_handler') == 'user')
444
		@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...
445
	@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...
446
447
	if (empty($smcFunc))
448
		$smcFunc = array();
449
450
	// We need this for authentication and some upgrade code
451
	require_once($sourcedir . '/Subs-Auth.php');
452
	require_once($sourcedir . '/Class-Package.php');
453
454
	$smcFunc['strtolower'] = 'smf_strtolower';
455
456
	// Initialize everything...
457
	initialize_inputs();
458
459
	// Get the database going!
460
	if (empty($db_type) || $db_type == 'mysqli')
461
	{
462
		$db_type = 'mysql';
463
		// If overriding $db_type, need to set its settings.php entry too
464
		$changes = array();
465
		$changes['db_type'] = '\'mysql\'';
466
		require_once($sourcedir . '/Subs-Admin.php');
467
		updateSettingsFile($changes);
468
	}
469
470
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
471
	{
472
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
473
474
		// Make the connection...
475
		if (empty($db_connection))
476
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, array('non_fatal' => true));
477
		else
478
			// If we've returned here, ping/reconnect to be safe
479
			$smcFunc['db_ping']($db_connection);
480
481
		// Oh dear god!!
482
		if ($db_connection === null)
483
			die('Unable to connect to database - please check username and password are correct in Settings.php');
484
485
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
486
			$smcFunc['db_query']('', '
487
			SET NAMES {string:db_character_set}',
488
			array(
489
				'db_error_skip' => true,
490
				'db_character_set' => $db_character_set,
491
			)
492
		);
493
494
		// Load the modSettings data...
495
		$request = $smcFunc['db_query']('', '
496
			SELECT variable, value
497
			FROM {db_prefix}settings',
498
			array(
499
				'db_error_skip' => true,
500
			)
501
		);
502
		$modSettings = array();
503 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
504
			$modSettings[$row['variable']] = $row['value'];
505
		$smcFunc['db_free_result']($request);
506
	}
507
	else
508
	{
509
		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.');
510
	}
511
512
	require_once($sourcedir . '/Subs.php');
513
514
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
515
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
516
	{
517
		require_once($sourcedir . '/QueryString.php');
518
		cleanRequest();
519
	}
520
521
	if (!isset($_GET['substep']))
522
		$_GET['substep'] = 0;
523
}
524
525
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 (L145-257) is considered.

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

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

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

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
526
{
527
	global $start_time, $db_type;
528
529
	$start_time = time();
530
531
	umask(0);
532
533
	ob_start();
534
535
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
536
	ignore_user_abort(true);
537
538
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
539
	if (isset($_GET['delete']))
540
	{
541
		@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...
542
543
		// And the extra little files ;).
544
		@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...
545
		@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...
546
		@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...
547
		@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...
548
		@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...
549
550
		$dh = opendir(dirname(__FILE__));
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $dh. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
551
		while ($file = readdir($dh))
552
		{
553
			if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
554
				@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...
555
		}
556
		closedir($dh);
557
558
		// Legacy files while we're at it. NOTE: We only touch files we KNOW shouldn't be there.
559
		// 1.1 Sources files not in 2.0+
560
		@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...
561
		// 1.1 Templates that don't exist any more (e.g. renamed)
562
		@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...
563
		@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...
564
		// 1.1 JS files were stored in the main theme folder, but in 2.0+ are in the scripts/ folder
565
		@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...
566
		@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...
567
		@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...
568
		@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...
569
		@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...
570
571
		// 2.0 Sources files not in 2.1+
572
		@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...
573
		@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...
574
575
		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');
576
		exit;
577
	}
578
579
	// Something is causing this to happen, and it's annoying.  Stop it.
580
	$temp = 'upgrade_php?step';
581
	while (strlen($temp) > 4)
582
	{
583
		if (isset($_GET[$temp]))
584
			unset($_GET[$temp]);
585
		$temp = substr($temp, 1);
586
	}
587
588
	// Force a step, defaulting to 0.
589
	$_GET['step'] = (int) @$_GET['step'];
590
	$_GET['substep'] = (int) @$_GET['substep'];
591
}
592
593
// Step 0 - Let's welcome them in and ask them to login!
594
function WelcomeLogin()
595
{
596
	global $boarddir, $sourcedir, $modSettings, $cachedir, $upgradeurl, $upcontext;
597
	global $smcFunc, $db_type, $databases, $boardurl;
598
599
	// We global $txt here so that the language files can add to them. This variable is NOT unused.
600
	global $txt;
601
602
	$upcontext['sub_template'] = 'welcome_message';
603
604
	// Check for some key files - one template, one language, and a new and an old source file.
605
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
606
		&& @file_exists($sourcedir . '/QueryString.php')
607
		&& @file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')
608
		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
609
610
	// Need legacy scripts?
611 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.1)
612
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
613 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.0)
614
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-1.sql');
615 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 1.1)
616
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
617
618
	// We don't need "-utf8" files anymore...
619
	$upcontext['language'] = str_ireplace('-utf8', '', $upcontext['language']);
620
621
	// This needs to exist!
622
	if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
623
		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>]');
624
	else
625
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
626
627
	if (!$check)
628
		// 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.
629
		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.');
630
631
	// Do they meet the install requirements?
632
	if (!php_version_check())
633
		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.');
634
635
	if (!db_version_check())
636
		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.');
637
638
	// Do some checks to make sure they have proper privileges
639
	db_extend('packages');
640
641
	// CREATE
642
	$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');
643
644
	// ALTER
645
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
646
647
	// DROP
648
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
649
650
	// Sorry... we need CREATE, ALTER and DROP
651 View Code Duplication
	if (!$create || !$alter || !$drop)
652
		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.');
653
654
	// Do a quick version spot check.
655
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
656
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
657
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
658
		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.');
659
660
	// What absolutely needs to be writable?
661
	$writable_files = array(
662
		$boarddir . '/Settings.php',
663
		$boarddir . '/Settings_bak.php',
664
		$boarddir . '/db_last_error.php',
665
		$modSettings['theme_dir'] . '/css/minified.css',
666
		$modSettings['theme_dir'] . '/scripts/minified.js',
667
		$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
668
	);
669
670
	// Do we need to add this setting?
671
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
672
673
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] . '/custom_avatar';
674
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
675
676
	// This little fellow has to cooperate...
677
	quickFileWritable($custom_av_dir);
678
679
	// Are we good now?
680
	if (!is_writable($custom_av_dir))
681
		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));
682
	elseif ($need_settings_update)
683
	{
684
		if (!function_exists('cache_put_data'))
685
			require_once($sourcedir . '/Load.php');
686
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
687
		updateSettings(array('custom_avatar_url' => $custom_av_url));
688
	}
689
690
	require_once($sourcedir . '/Security.php');
691
692
	// Check the cache directory.
693
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
694
	if (!file_exists($cachedir_temp))
695
		@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...
696
	if (!file_exists($cachedir_temp))
697
		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.');
698
699
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
700
		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>.');
701
	elseif (!isset($_GET['skiplang']))
702
	{
703
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
704
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
705
706
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
707
			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>]');
708
	}
709
710
	if (!makeFilesWritable($writable_files))
711
		return false;
712
713
	// Check agreement.txt. (it may not exist, in which case $boarddir must be writable.)
714 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
715
		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.');
716
717
	// Upgrade the agreement.
718
	elseif (isset($modSettings['agreement']))
719
	{
720
		$fp = fopen($boarddir . '/agreement.txt', 'w');
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $fp. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
721
		fwrite($fp, $modSettings['agreement']);
722
		fclose($fp);
723
	}
724
725
	// We're going to check that their board dir setting is right in case they've been moving stuff around.
726
	if (strtr($boarddir, array('/' => '', '\\' => '')) != strtr(dirname(__FILE__), array('/' => '', '\\' => '')))
727
		$upcontext['warning'] = '
728
			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>
729
			<ul>
730
				<li>Board Directory: ' . $boarddir . '</li>
731
				<li>Source Directory: ' . $boarddir . '</li>
732
				<li>Cache Directory: ' . $cachedir_temp . '</li>
733
			</ul>
734
			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.';
735
736
	// Confirm mbstring is loaded...
737
	if (!extension_loaded('mbstring'))
738
		return throw_error($txt['install_no_mbstring']);
739
740
	// Check for https stream support.
741
	$supported_streams = stream_get_wrappers();
742
	if (!in_array('https', $supported_streams))
743
		$upcontext['custom_warning'] = $txt['install_no_https'];
744
745
	// Either we're logged in or we're going to present the login.
746
	if (checkLogin())
747
		return true;
748
749
	$upcontext += createToken('login');
750
751
	return false;
752
}
753
754
// Step 0.5: Does the login work?
755
function checkLogin()
756
{
757
	global $modSettings, $upcontext, $disable_security;
758
	global $smcFunc, $db_type, $support_js;
759
760
	// Don't bother if the security is disabled.
761
	if ($disable_security)
762
		return true;
763
764
	// Are we trying to login?
765
	if (isset($_POST['contbutt']) && (!empty($_POST['user'])))
766
	{
767
		// If we've disabled security pick a suitable name!
768
		if (empty($_POST['user']))
769
			$_POST['user'] = 'Administrator';
770
771
		// Before 2.0 these column names were different!
772
		$oldDB = false;
773
		if (empty($db_type) || $db_type == 'mysql')
774
		{
775
			$request = $smcFunc['db_query']('', '
776
				SHOW COLUMNS
777
				FROM {db_prefix}members
778
				LIKE {string:member_name}',
779
				array(
780
					'member_name' => 'memberName',
781
					'db_error_skip' => true,
782
				)
783
			);
784
			if ($smcFunc['db_num_rows']($request) != 0)
785
				$oldDB = true;
786
			$smcFunc['db_free_result']($request);
787
		}
788
789
		// Get what we believe to be their details.
790
		if (!$disable_security)
791
		{
792
			if ($oldDB)
793
				$request = $smcFunc['db_query']('', '
794
					SELECT id_member, memberName AS member_name, passwd, id_group,
795
					additionalGroups AS additional_groups, lngfile
796
					FROM {db_prefix}members
797
					WHERE memberName = {string:member_name}',
798
					array(
799
						'member_name' => $_POST['user'],
800
						'db_error_skip' => true,
801
					)
802
				);
803
			else
804
				$request = $smcFunc['db_query']('', '
805
					SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile
806
					FROM {db_prefix}members
807
					WHERE member_name = {string:member_name}',
808
					array(
809
						'member_name' => $_POST['user'],
810
						'db_error_skip' => true,
811
					)
812
				);
813
			if ($smcFunc['db_num_rows']($request) != 0)
814
			{
815
				list ($id_member, $name, $password, $id_group, $addGroups, $user_language) = $smcFunc['db_fetch_row']($request);
816
817
				$groups = explode(',', $addGroups);
818
				$groups[] = $id_group;
819
820
				foreach ($groups as $k => $v)
821
					$groups[$k] = (int) $v;
822
823
				$sha_passwd = sha1(strtolower($name) . un_htmlspecialchars($_REQUEST['passwrd']));
824
825
				// We don't use "-utf8" anymore...
826
				$user_language = str_ireplace('-utf8', '', $user_language);
827
			}
828
			else
829
				$upcontext['username_incorrect'] = true;
830
			$smcFunc['db_free_result']($request);
831
		}
832
		$upcontext['username'] = $_POST['user'];
833
834
		// Track whether javascript works!
835
		if (!empty($_POST['js_works']))
836
		{
837
			$upcontext['upgrade_status']['js'] = 1;
838
			$support_js = 1;
839
		}
840
		else
841
			$support_js = 0;
842
843
		// Note down the version we are coming from.
844
		if (!empty($modSettings['smfVersion']) && empty($upcontext['user']['version']))
845
			$upcontext['user']['version'] = $modSettings['smfVersion'];
846
847
		// Didn't get anywhere?
848
		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...
849
		{
850
			// MD5?
851
			$md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user']));
852
			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...
853
			{
854
				$upcontext['password_failed'] = true;
855
				// Disable the hashing this time.
856
				$upcontext['disable_login_hashing'] = true;
857
			}
858
		}
859
860
		if ((empty($upcontext['password_failed']) && !empty($name)) || $disable_security)
861
		{
862
			// Set the password.
863
			if (!$disable_security)
864
			{
865
				// Do we actually have permission?
866
				if (!in_array(1, $groups))
867
				{
868
					$request = $smcFunc['db_query']('', '
869
						SELECT permission
870
						FROM {db_prefix}permissions
871
						WHERE id_group IN ({array_int:groups})
872
							AND permission = {string:admin_forum}',
873
						array(
874
							'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...
875
							'admin_forum' => 'admin_forum',
876
							'db_error_skip' => true,
877
						)
878
					);
879
					if ($smcFunc['db_num_rows']($request) == 0)
880
						return throw_error('You need to be an admin to perform an upgrade!');
881
					$smcFunc['db_free_result']($request);
882
				}
883
884
				$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...
885
				$upcontext['user']['name'] = $name;
886
			}
887
			else
888
			{
889
				$upcontext['user']['id'] = 1;
890
				$upcontext['user']['name'] = 'Administrator';
891
			}
892
			$upcontext['user']['pass'] = mt_rand(0, 60000);
893
			// This basically is used to match the GET variables to Settings.php.
894
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
895
896
			// Set the language to that of the user?
897
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
898
			{
899
				$user_language = basename($user_language, '.lng');
900
				$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
901
				preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
902
903
				if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
904
					$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'] . '.';
905
				elseif (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . basename($user_language, '.lng') . '.php'))
906
					$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'] . '.';
907
				else
908
				{
909
					// Set this as the new language.
910
					$upcontext['language'] = $user_language;
911
					$upcontext['upgrade_status']['lang'] = $upcontext['language'];
912
913
					// Include the file.
914
					require_once($modSettings['theme_dir'] . '/languages/Install.' . $user_language . '.php');
915
				}
916
			}
917
918
			// If we're resuming set the step and substep to be correct.
919
			if (isset($_POST['cont']))
920
			{
921
				$upcontext['current_step'] = $upcontext['user']['step'];
922
				$_GET['substep'] = $upcontext['user']['substep'];
923
			}
924
925
			return true;
926
		}
927
	}
928
929
	return false;
930
}
931
932
// Step 1: Do the maintenance and backup.
933
function UpgradeOptions()
934
{
935
	global $db_prefix, $command_line, $modSettings, $is_debug, $smcFunc, $packagesdir, $tasksdir, $language;
936
	global $boarddir, $boardurl, $sourcedir, $maintenance, $cachedir, $upcontext, $db_type, $db_server;
937
938
	$upcontext['sub_template'] = 'upgrade_options';
939
	$upcontext['page_title'] = 'Upgrade Options';
940
941
	db_extend('packages');
942
	$upcontext['karma_installed'] = array('good' => false, 'bad' => false);
943
	$member_columns = $smcFunc['db_list_columns']('{db_prefix}members');
944
945
	$upcontext['karma_installed']['good'] = in_array('karma_good', $member_columns);
946
	$upcontext['karma_installed']['bad'] = in_array('karma_bad', $member_columns);
947
948
	unset($member_columns);
949
950
	// If we've not submitted then we're done.
951
	if (empty($_POST['upcont']))
952
		return false;
953
954
	// Firstly, if they're enabling SM stat collection just do it.
955
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']))
956
	{
957
		$upcontext['allow_sm_stats'] = true;
958
959
		// Don't register if we still have a key.
960
		if (empty($modSettings['sm_stats_key']))
961
		{
962
			// Attempt to register the site etc.
963
			$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $fp. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
964 View Code Duplication
			if ($fp)
965
			{
966
				$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
967
				$out .= 'Host: www.simplemachines.org' . "\r\n";
968
				$out .= 'Connection: Close' . "\r\n\r\n";
969
				fwrite($fp, $out);
970
971
				$return_data = '';
972
				while (!feof($fp))
973
					$return_data .= fgets($fp, 128);
974
975
				fclose($fp);
976
977
				// Get the unique site ID.
978
				preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ID. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
979
980
				if (!empty($ID[1]))
981
					$smcFunc['db_insert']('replace',
982
						$db_prefix . 'settings',
983
						array('variable' => 'string', 'value' => 'string'),
984
						array(
985
							array('sm_stats_key', $ID[1]),
986
							array('enable_sm_stats', 1),
987
						),
988
						array('variable')
989
					);
990
			}
991
		}
992
		else
993
		{
994
			$smcFunc['db_insert']('replace',
995
				$db_prefix . 'settings',
996
				array('variable' => 'string', 'value' => 'string'),
997
				array('enable_sm_stats', 1),
998
				array('variable')
999
			);
1000
		}
1001
	}
1002
	// Don't remove stat collection unless we unchecked the box for real, not from the loop.
1003 View Code Duplication
	elseif (empty($_POST['stats']) && empty($upcontext['allow_sm_stats']))
1004
		$smcFunc['db_query']('', '
1005
			DELETE FROM {db_prefix}settings
1006
			WHERE variable = {string:enable_sm_stats}',
1007
			array(
1008
				'enable_sm_stats' => 'enable_sm_stats',
1009
				'db_error_skip' => true,
1010
			)
1011
		);
1012
1013
	// Deleting old karma stuff?
1014
	if (!empty($_POST['delete_karma']))
1015
	{
1016
		// Delete old settings vars.
1017
		$smcFunc['db_query']('', '
1018
			DELETE FROM {db_prefix}settings
1019
			WHERE variable IN ({array_string:karma_vars})',
1020
			array(
1021
				'karma_vars' => array('karmaMode', 'karmaTimeRestrictAdmins', 'karmaWaitTime', 'karmaMinPosts', 'karmaLabel', 'karmaSmiteLabel', 'karmaApplaudLabel'),
1022
			)
1023
		);
1024
1025
		// Cleaning up old karma member settings.
1026
		if ($upcontext['karma_installed']['good'])
1027
			$smcFunc['db_query']('', '
1028
				ALTER TABLE {db_prefix}members
1029
				DROP karma_good',
1030
				array()
1031
			);
1032
1033
		// Does karma bad was enable?
1034
		if ($upcontext['karma_installed']['bad'])
1035
			$smcFunc['db_query']('', '
1036
				ALTER TABLE {db_prefix}members
1037
				DROP karma_bad',
1038
				array()
1039
			);
1040
1041
		// Cleaning up old karma permissions.
1042
		$smcFunc['db_query']('', '
1043
			DELETE FROM {db_prefix}permissions
1044
			WHERE permission = {string:karma_vars}',
1045
			array(
1046
				'karma_vars' => 'karma_edit',
1047
			)
1048
		);
1049
	}
1050
1051
	// Emptying the error log?
1052
	if (!empty($_POST['empty_error']))
1053
		$smcFunc['db_query']('truncate_table', '
1054
			TRUNCATE {db_prefix}log_errors',
1055
			array(
1056
			)
1057
		);
1058
1059
	$changes = array();
1060
1061
	// Add proxy settings.
1062
	if (!isset($GLOBALS['image_proxy_maxsize']))
1063
		$changes += array(
1064
			'image_proxy_secret' => '\'' . substr(sha1(mt_rand()), 0, 20) . '\'',
1065
			'image_proxy_maxsize' => 5190,
1066
			'image_proxy_enabled' => 0,
1067
		);
1068
1069
	// If we're overriding the language follow it through.
1070 View Code Duplication
	if (isset($_GET['lang']) && file_exists($modSettings['theme_dir'] . '/languages/index.' . $_GET['lang'] . '.php'))
1071
		$changes['language'] = '\'' . $_GET['lang'] . '\'';
1072
1073
	if (!empty($_POST['maint']))
1074
	{
1075
		$changes['maintenance'] = '2';
1076
		// Remember what it was...
1077
		$upcontext['user']['main'] = $maintenance;
1078
1079
		if (!empty($_POST['maintitle']))
1080
		{
1081
			$changes['mtitle'] = '\'' . addslashes($_POST['maintitle']) . '\'';
1082
			$changes['mmessage'] = '\'' . addslashes($_POST['mainmessage']) . '\'';
1083
		}
1084
		else
1085
		{
1086
			$changes['mtitle'] = '\'Upgrading the forum...\'';
1087
			$changes['mmessage'] = '\'Don\\\'t worry, we will be back shortly with an updated forum.  It will only be a minute ;).\'';
1088
		}
1089
	}
1090
1091
	if ($command_line)
1092
		echo ' * Updating Settings.php...';
1093
1094
	// Fix some old paths.
1095 View Code Duplication
	if (substr($boarddir, 0, 1) == '.')
1096
		$changes['boarddir'] = '\'' . fixRelativePath($boarddir) . '\'';
1097
1098 View Code Duplication
	if (substr($sourcedir, 0, 1) == '.')
1099
		$changes['sourcedir'] = '\'' . fixRelativePath($sourcedir) . '\'';
1100
1101
	if (empty($cachedir) || substr($cachedir, 0, 1) == '.')
1102
		$changes['cachedir'] = '\'' . fixRelativePath($boarddir) . '/cache\'';
1103
1104
	// If they have a "host:port" setup for the host, split that into separate values
1105
	// You should never have a : in the hostname if you're not on MySQL, but better safe than sorry
1106
	if (strpos($db_server, ':') !== false && $db_type == 'mysql')
1107
	{
1108
		list ($db_server, $db_port) = explode(':', $db_server);
1109
1110
		$changes['db_server'] = '\'' . $db_server . '\'';
1111
1112
		// Only set this if we're not using the default port
1113
		if ($db_port != ini_get('mysqli.default_port'))
1114
			$changes['db_port'] = (int) $db_port;
1115
	}
1116
	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...
1117
	{
1118
		// If db_port is set and is the same as the default, set it to ''
1119
		if ($db_type == 'mysql')
1120
		{
1121
			if ($db_port == ini_get('mysqli.default_port'))
1122
				$changes['db_port'] = '\'\'';
1123
			elseif ($db_type == 'postgresql' && $db_port == 5432)
1124
				$changes['db_port'] = '\'\'';
1125
		}
1126
	}
1127
1128
	// Maybe we haven't had this option yet?
1129
	if (empty($packagesdir))
1130
		$changes['packagesdir'] = '\'' . fixRelativePath($boarddir) . '/Packages\'';
1131
1132
	// Add support for $tasksdir var.
1133
	if (empty($tasksdir))
1134
		$changes['tasksdir'] = '\'' . fixRelativePath($sourcedir) . '/tasks\'';
1135
1136
	// Make sure we fix the language as well.
1137
	if (stristr($language, '-utf8'))
1138
		$changes['language'] = '\'' . str_ireplace('-utf8', '', $language) . '\'';
1139
1140
	// @todo Maybe change the cookie name if going to 1.1, too?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
1141
1142
	// Update Settings.php with the new settings.
1143
	require_once($sourcedir . '/Subs-Admin.php');
1144
	updateSettingsFile($changes);
1145
1146
	if ($command_line)
1147
		echo ' Successful.' . "\n";
1148
1149
	// Are we doing debug?
1150
	if (isset($_POST['debug']))
1151
	{
1152
		$upcontext['upgrade_status']['debug'] = true;
1153
		$is_debug = true;
1154
	}
1155
1156
	// If we're not backing up then jump one.
1157
	if (empty($_POST['backup']))
1158
		$upcontext['current_step']++;
1159
1160
	// If we've got here then let's proceed to the next step!
1161
	return true;
1162
}
1163
1164
// Backup the database - why not...
1165
function BackupDatabase()
1166
{
1167
	global $upcontext, $db_prefix, $command_line, $support_js, $file_steps, $smcFunc;
1168
1169
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1170
	$upcontext['page_title'] = 'Backup Database';
1171
1172
	// Done it already - js wise?
1173
	if (!empty($_POST['backup_done']))
1174
		return true;
1175
1176
	// Some useful stuff here.
1177
	db_extend();
1178
1179
	// Might need this as well
1180
	db_extend('packages');
1181
1182
	// Get all the table names.
1183
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1184
	$db = preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? strtr($match[1], array('`' => '')) : false;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $db. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
1185
	$tables = $smcFunc['db_list_tables']($db, $filter);
1186
1187
	$table_names = array();
1188
	foreach ($tables as $table)
1189
		if (substr($table, 0, 7) !== 'backup_')
1190
			$table_names[] = $table;
1191
1192
	$upcontext['table_count'] = count($table_names);
1193
	$upcontext['cur_table_num'] = $_GET['substep'];
1194
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1195
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1196
	// For non-java auto submit...
1197
	$file_steps = $upcontext['table_count'];
1198
1199
	// What ones have we already done?
1200 View Code Duplication
	foreach ($table_names as $id => $table)
1201
		if ($id < $_GET['substep'])
1202
			$upcontext['previous_tables'][] = $table;
1203
1204
	if ($command_line)
1205
		echo 'Backing Up Tables.';
1206
1207
	// If we don't support javascript we backup here.
1208
	if (!$support_js || isset($_GET['xml']))
1209
	{
1210
		// Backup each table!
1211
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1212
		{
1213
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1214
			$upcontext['cur_table_num'] = $substep + 1;
1215
1216
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1217
1218
			// Do we need to pause?
1219
			nextSubstep($substep);
1220
1221
			backupTable($table_names[$substep]);
1222
1223
			// If this is XML to keep it nice for the user do one table at a time anyway!
1224
			if (isset($_GET['xml']))
1225
				return upgradeExit();
1226
		}
1227
1228
		if ($command_line)
1229
		{
1230
			echo "\n" . ' Successful.\'' . "\n";
1231
			flush();
1232
		}
1233
		$upcontext['step_progress'] = 100;
1234
1235
		$_GET['substep'] = 0;
1236
		// Make sure we move on!
1237
		return true;
1238
	}
1239
1240
	// Either way next place to post will be database changes!
1241
	$_GET['substep'] = 0;
1242
	return false;
1243
}
1244
1245
// Backup one table...
1246
function backupTable($table)
1247
{
1248
	global $command_line, $db_prefix, $smcFunc;
1249
1250
	if ($command_line)
1251
	{
1252
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1253
		flush();
1254
	}
1255
1256
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1257
1258
	if ($command_line)
1259
		echo ' done.';
1260
}
1261
1262
// Step 2: Everything.
1263
function DatabaseChanges()
1264
{
1265
	global $db_prefix, $modSettings, $smcFunc;
1266
	global $upcontext, $support_js, $db_type;
1267
1268
	// Have we just completed this?
1269
	if (!empty($_POST['database_done']))
1270
		return true;
1271
1272
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1273
	$upcontext['page_title'] = 'Database Changes';
1274
1275
	// All possible files.
1276
	// Name, < version, insert_on_complete
1277
	$files = array(
1278
		array('upgrade_1-0.sql', '1.1', '1.1 RC0'),
1279
		array('upgrade_1-1.sql', '2.0', '2.0 a'),
1280
		array('upgrade_2-0_' . $db_type . '.sql', '2.1', '2.1 dev0'),
1281
		array('upgrade_2-1_' . $db_type . '.sql', '3.0', SMF_VERSION),
1282
	);
1283
1284
	// How many files are there in total?
1285
	if (isset($_GET['filecount']))
1286
		$upcontext['file_count'] = (int) $_GET['filecount'];
1287
	else
1288
	{
1289
		$upcontext['file_count'] = 0;
1290
		foreach ($files as $file)
1291
		{
1292
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1293
				$upcontext['file_count']++;
1294
		}
1295
	}
1296
1297
	// Do each file!
1298
	$did_not_do = count($files) - $upcontext['file_count'];
1299
	$upcontext['step_progress'] = 0;
1300
	$upcontext['cur_file_num'] = 0;
1301
	foreach ($files as $file)
1302
	{
1303
		if ($did_not_do)
1304
			$did_not_do--;
1305
		else
1306
		{
1307
			$upcontext['cur_file_num']++;
1308
			$upcontext['cur_file_name'] = $file[0];
1309
			// Do we actually need to do this still?
1310
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1311
			{
1312
				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1313
				if ($nextFile)
1314
				{
1315
					// Only update the version of this if complete.
1316
					$smcFunc['db_insert']('replace',
1317
						$db_prefix . 'settings',
1318
						array('variable' => 'string', 'value' => 'string'),
1319
						array('smfVersion', $file[2]),
1320
						array('variable')
1321
					);
1322
1323
					$modSettings['smfVersion'] = $file[2];
1324
				}
1325
1326
				// If this is XML we only do this stuff once.
1327
				if (isset($_GET['xml']))
1328
				{
1329
					// Flag to move on to the next.
1330
					$upcontext['completed_step'] = true;
1331
					// Did we complete the whole file?
1332
					if ($nextFile)
1333
						$upcontext['current_debug_item_num'] = -1;
1334
					return upgradeExit();
1335
				}
1336
				elseif ($support_js)
1337
					break;
1338
			}
1339
			// Set the progress bar to be right as if we had - even if we hadn't...
1340
			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1341
		}
1342
	}
1343
1344
	$_GET['substep'] = 0;
1345
	// So the template knows we're done.
1346
	if (!$support_js)
1347
	{
1348
		$upcontext['changes_complete'] = true;
1349
1350
		return true;
1351
	}
1352
	return false;
1353
}
1354
1355
1356
// Delete the damn thing!
1357
function DeleteUpgrade()
1358
{
1359
	global $command_line, $language, $upcontext, $sourcedir, $forum_version, $user_info, $maintenance, $smcFunc, $db_type;
1360
1361
	// Now it's nice to have some of the basic SMF source files.
1362
	if (!isset($_GET['ssi']) && !$command_line)
1363
		redirectLocation('&ssi=1');
1364
1365
	$upcontext['sub_template'] = 'upgrade_complete';
1366
	$upcontext['page_title'] = 'Upgrade Complete';
1367
1368
	$endl = $command_line ? "\n" : '<br>' . "\n";
1369
1370
	$changes = array(
1371
		'language' => '\'' . (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language) . '\'',
1372
		'db_error_send' => '1',
1373
		'upgradeData' => '\'\'',
1374
	);
1375
1376
	// Are we in maintenance mode?
1377
	if (isset($upcontext['user']['main']))
1378
	{
1379
		if ($command_line)
1380
			echo ' * ';
1381
		$upcontext['removed_maintenance'] = true;
1382
		$changes['maintenance'] = $upcontext['user']['main'];
1383
	}
1384
	// Otherwise if somehow we are in 2 let's go to 1.
1385
	elseif (!empty($maintenance) && $maintenance == 2)
1386
		$changes['maintenance'] = 1;
1387
1388
	// Wipe this out...
1389
	$upcontext['user'] = array();
1390
1391
	require_once($sourcedir . '/Subs-Admin.php');
1392
	updateSettingsFile($changes);
1393
1394
	// Clean any old cache files away.
1395
	upgrade_clean_cache();
1396
1397
	// Can we delete the file?
1398
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1399
1400
	// Now is the perfect time to fetch the SM files.
1401
	if ($command_line)
1402
		cli_scheduled_fetchSMfiles();
1403
	else
1404
	{
1405
		require_once($sourcedir . '/ScheduledTasks.php');
1406
		$forum_version = SMF_VERSION; // The variable is usually defined in index.php so lets just use the constant to do it for us.
1407
		scheduled_fetchSMfiles(); // Now go get those files!
1408
	}
1409
1410
	// Log what we've done.
1411
	if (empty($user_info['id']))
1412
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
1413
1414
	// Log the action manually, so CLI still works.
1415
	$smcFunc['db_insert']('',
1416
		'{db_prefix}log_actions',
1417
		array(
1418
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'inet', 'action' => 'string',
1419
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
1420
		),
1421
		array(
1422
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
1423
			0, 0, 0, json_encode(array('version' => $forum_version, 'member' => $user_info['id'])),
1424
		),
1425
		array('id_action')
1426
	);
1427
	$user_info['id'] = 0;
1428
1429
	// Save the current database version.
1430
	$server_version = $smcFunc['db_server_info']();
1431 View Code Duplication
	if ($db_type == 'mysql' && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51')))
1432
		updateSettings(array('db_mysql_group_by_fix' => '1'));
1433
1434
	if ($command_line)
1435
	{
1436
		echo $endl;
1437
		echo 'Upgrade Complete!', $endl;
1438
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
1439
		exit;
1440
	}
1441
1442
	// Make sure it says we're done.
1443
	$upcontext['overall_percent'] = 100;
1444
	if (isset($upcontext['step_progress']))
1445
		unset($upcontext['step_progress']);
1446
1447
	$_GET['substep'] = 0;
1448
	return false;
1449
}
1450
1451
// Just like the built in one, but setup for CLI to not use themes.
1452
function cli_scheduled_fetchSMfiles()
1453
{
1454
	global $sourcedir, $language, $forum_version, $modSettings, $smcFunc;
1455
1456
	if (empty($modSettings['time_format']))
1457
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1458
1459
	// What files do we want to get
1460
	$request = $smcFunc['db_query']('', '
1461
		SELECT id_file, filename, path, parameters
1462
		FROM {db_prefix}admin_info_files',
1463
		array(
1464
		)
1465
	);
1466
1467
	$js_files = array();
1468 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
1469
	{
1470
		$js_files[$row['id_file']] = array(
1471
			'filename' => $row['filename'],
1472
			'path' => $row['path'],
1473
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode($forum_version)),
1474
		);
1475
	}
1476
	$smcFunc['db_free_result']($request);
1477
1478
	// We're gonna need fetch_web_data() to pull this off.
1479
	require_once($sourcedir . '/Subs-Package.php');
1480
1481
	foreach ($js_files as $ID_FILE => $file)
1482
	{
1483
		// Create the url
1484
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'https://www.simplemachines.org' : '';
1485
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1486
1487
		// Get the file
1488
		$file_data = fetch_web_data($url);
1489
1490
		// If we got an error - give up - the site might be down.
1491
		if ($file_data === false)
1492
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
1493
1494
		// Save the file to the database.
1495
		$smcFunc['db_query']('substring', '
1496
			UPDATE {db_prefix}admin_info_files
1497
			SET data = SUBSTRING({string:file_data}, 1, 65534)
1498
			WHERE id_file = {int:id_file}',
1499
			array(
1500
				'id_file' => $ID_FILE,
1501
				'file_data' => $file_data,
1502
			)
1503
		);
1504
	}
1505
	return true;
1506
}
1507
1508
function convertSettingsToTheme()
1509
{
1510
	global $db_prefix, $modSettings, $smcFunc;
1511
1512
	$values = array(
1513
		'show_latest_member' => @$GLOBALS['showlatestmember'],
1514
		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
1515
		'show_modify' => @$GLOBALS['showmodify'],
1516
		'show_user_images' => @$GLOBALS['showuserpic'],
1517
		'show_blurb' => @$GLOBALS['showusertext'],
1518
		'show_gender' => @$GLOBALS['showgenderimage'],
1519
		'show_newsfader' => @$GLOBALS['shownewsfader'],
1520
		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
1521
		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
1522
		'linktree_link' => @$GLOBALS['curposlinks'],
1523
		'show_profile_buttons' => @$GLOBALS['profilebutton'],
1524
		'show_mark_read' => @$GLOBALS['showmarkread'],
1525
		'newsfader_time' => @$GLOBALS['fadertime'],
1526
		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
1527
		'enable_news' => @$GLOBALS['enable_news'],
1528
		'return_to_post' => @$modSettings['returnToPost'],
1529
	);
1530
1531
	$themeData = array();
1532
	foreach ($values as $variable => $value)
1533
	{
1534
		if (!isset($value) || $value === null)
1535
			$value = 0;
1536
1537
		$themeData[] = array(0, 1, $variable, $value);
1538
	}
1539 View Code Duplication
	if (!empty($themeData))
1540
	{
1541
		$smcFunc['db_insert']('ignore',
1542
			$db_prefix . 'themes',
1543
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
1544
			$themeData,
1545
			array('id_member', 'id_theme', 'variable')
1546
		);
1547
	}
1548
}
1549
1550
// This function only works with MySQL but that's fine as it is only used for v1.0.
1551
function convertSettingstoOptions()
1552
{
1553
	global $modSettings, $smcFunc;
1554
1555
	// Format: new_setting -> old_setting_name.
1556
	$values = array(
1557
		'calendar_start_day' => 'cal_startmonday',
1558
		'view_newest_first' => 'viewNewestFirst',
1559
		'view_newest_pm_first' => 'viewNewestFirst',
1560
	);
1561
1562
	foreach ($values as $variable => $value)
1563
	{
1564
		if (empty($modSettings[$value[0]]))
1565
			continue;
1566
1567
		$smcFunc['db_query']('', '
1568
			INSERT IGNORE INTO {db_prefix}themes
1569
				(id_member, id_theme, variable, value)
1570
			SELECT id_member, 1, {string:variable}, {string:value}
1571
			FROM {db_prefix}members',
1572
			array(
1573
				'variable' => $variable,
1574
				'value' => $modSettings[$value[0]],
1575
				'db_error_skip' => true,
1576
			)
1577
		);
1578
1579
		$smcFunc['db_query']('', '
1580
			INSERT IGNORE INTO {db_prefix}themes
1581
				(id_member, id_theme, variable, value)
1582
			VALUES (-1, 1, {string:variable}, {string:value})',
1583
			array(
1584
				'variable' => $variable,
1585
				'value' => $modSettings[$value[0]],
1586
				'db_error_skip' => true,
1587
			)
1588
		);
1589
	}
1590
}
1591
1592
function php_version_check()
1593
{
1594
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
1595
}
1596
1597
function db_version_check()
1598
{
1599
	global $db_type, $databases;
1600
1601
	$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...
1602
	$curver = preg_replace('~\-.+?$~', '', $curver);
1603
1604
	return version_compare($databases[$db_type]['version'], $curver, '<=');
1605
}
1606
1607
function fixRelativePath($path)
1608
{
1609
	global $install_path;
1610
1611
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
1612
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
1613
}
1614
1615
function parse_sql($filename)
1616
{
1617
	global $db_prefix, $db_collation, $boarddir, $boardurl, $command_line, $file_steps, $step_progress, $custom_warning;
1618
	global $upcontext, $support_js, $is_debug, $db_type, $db_character_set;
1619
1620
/*
1621
	Failure allowed on:
1622
		- INSERT INTO but not INSERT IGNORE INTO.
1623
		- UPDATE IGNORE but not UPDATE.
1624
		- ALTER TABLE and ALTER IGNORE TABLE.
1625
		- DROP TABLE.
1626
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
1627
1628
	If a comment...
1629
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
1630
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
1631
		- is only ---#, it is "done." and then a break - only shown in debug.
1632
		- begins with ---{ it is a code block terminating at ---}.
1633
1634
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
1635
1636
	Replaces the following variables:
1637
		- {$boarddir}
1638
		- {$boardurl}
1639
		- {$db_prefix}
1640
		- {$db_collation}
1641
*/
1642
1643
	// May want to use extended functionality.
1644
	db_extend();
1645
	db_extend('packages');
1646
1647
	// Our custom error handler - does nothing but does stop public errors from XML!
1648
	set_error_handler(
1649
		function ($errno, $errstr, $errfile, $errline) use ($support_js)
1650
		{
1651
			if ($support_js)
1652
				return true;
1653
			else
1654
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
1655
		}
1656
	);
1657
1658
	// If we're on MySQL, set {db_collation}; this approach is used throughout upgrade_2-0_mysql.php to set new tables to utf8
1659
	// Note it is expected to be in the format: ENGINE=MyISAM{$db_collation};
1660
	if ($db_type == 'mysql')
1661
		$db_collation = ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1662
	else
1663
		$db_collation = '';
1664
1665
	$endl = $command_line ? "\n" : '<br>' . "\n";
1666
1667
	$lines = file($filename);
1668
1669
	$current_type = 'sql';
1670
	$current_data = '';
1671
	$substep = 0;
1672
	$last_step = '';
1673
1674
	// Make sure all newly created tables will have the proper characters set; this approach is used throughout upgrade_2-1_mysql.php
1675
	if (isset($db_character_set) && $db_character_set === 'utf8')
1676
		$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
1677
1678
	// Count the total number of steps within this file - for progress.
1679
	$file_steps = substr_count(implode('', $lines), '---#');
1680
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
1681
	$upcontext['debug_items'] = $file_steps;
1682
	$upcontext['current_item_num'] = 0;
1683
	$upcontext['current_item_name'] = '';
1684
	$upcontext['current_debug_item_num'] = 0;
1685
	$upcontext['current_debug_item_name'] = '';
1686
	// This array keeps a record of what we've done in case java is dead...
1687
	$upcontext['actioned_items'] = array();
1688
1689
	$done_something = false;
1690
1691
	foreach ($lines as $line_number => $line)
1692
	{
1693
		$do_current = $substep >= $_GET['substep'];
1694
1695
		// Get rid of any comments in the beginning of the line...
1696
		if (substr(trim($line), 0, 2) === '/*')
1697
			$line = preg_replace('~/\*.+?\*/~', '', $line);
1698
1699
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
1700
		if ($is_debug && !$support_js && $command_line)
1701
			flush();
1702
1703
		if (trim($line) === '')
1704
			continue;
1705
1706
		if (trim(substr($line, 0, 3)) === '---')
1707
		{
1708
			$type = substr($line, 3, 1);
1709
1710
			// An error??
1711
			if (trim($current_data) != '' && $type !== '}')
1712
			{
1713
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
1714
				if ($command_line)
1715
					echo $upcontext['error_message'];
1716
			}
1717
1718
			if ($type == ' ')
1719
			{
1720
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
1721
				{
1722
					echo ' Successful.', $endl;
1723
					flush();
1724
				}
1725
1726
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
1727
				$upcontext['current_item_num']++;
1728
				$upcontext['current_item_name'] = $last_step;
1729
1730
				if ($do_current)
1731
				{
1732
					$upcontext['actioned_items'][] = $last_step;
1733
					if ($command_line)
1734
						echo ' * ';
1735
				}
1736
			}
1737
			elseif ($type == '#')
1738
			{
1739
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
1740
1741
				$upcontext['current_debug_item_num']++;
1742
				if (trim($line) != '---#')
1743
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
1744
1745
				// Have we already done something?
1746
				if (isset($_GET['xml']) && $done_something)
1747
				{
1748
					restore_error_handler();
1749
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
1750
				}
1751
1752
				if ($do_current)
1753
				{
1754
					if (trim($line) == '---#' && $command_line)
1755
						echo ' done.', $endl;
1756
					elseif ($command_line)
1757
						echo ' +++ ', rtrim(substr($line, 4));
1758
					elseif (trim($line) != '---#')
1759
					{
1760
						if ($is_debug)
1761
							$upcontext['actioned_items'][] = htmlspecialchars(rtrim(substr($line, 4)));
1762
					}
1763
				}
1764
1765
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
1766
				{
1767
					if ($command_line)
1768
						echo ' * ';
1769
					else
1770
						$upcontext['actioned_items'][] = $last_step;
1771
				}
1772
1773
				// Small step - only if we're actually doing stuff.
1774
				if ($do_current)
1775
					nextSubstep(++$substep);
1776
				else
1777
					$substep++;
1778
			}
1779
			elseif ($type == '{')
1780
				$current_type = 'code';
1781
			elseif ($type == '}')
1782
			{
1783
				$current_type = 'sql';
1784
1785
				if (!$do_current)
1786
				{
1787
					$current_data = '';
1788
					continue;
1789
				}
1790
1791
				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...
1792
				{
1793
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
1794
					if ($command_line)
1795
						echo $upcontext['error_message'];
1796
				}
1797
1798
				// Done with code!
1799
				$current_data = '';
1800
				$done_something = true;
1801
			}
1802
1803
			continue;
1804
		}
1805
1806
		$current_data .= $line;
1807
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
1808
		{
1809
			if ((!$support_js || isset($_GET['xml'])))
1810
			{
1811
				if (!$do_current)
1812
				{
1813
					$current_data = '';
1814
					continue;
1815
				}
1816
1817
				$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));
1818
1819
				upgrade_query($current_data);
1820
1821
				// @todo This will be how it kinda does it once mysql all stripped out - needed for postgre (etc).
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
1822
				/*
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...
1823
				$result = $smcFunc['db_query']('', $current_data, false, false);
1824
				// Went wrong?
1825
				if (!$result)
1826
				{
1827
					// Bit of a bodge - do we want the error?
1828
					if (!empty($upcontext['return_error']))
1829
					{
1830
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
1831
						return false;
1832
					}
1833
				}*/
1834
				$done_something = true;
1835
			}
1836
			$current_data = '';
1837
		}
1838
		// If this is xml based and we're just getting the item name then that's grand.
1839
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
1840
		{
1841
			restore_error_handler();
1842
			return false;
1843
		}
1844
1845
		// Clean up by cleaning any step info.
1846
		$step_progress = array();
1847
		$custom_warning = '';
1848
	}
1849
1850
	// Put back the error handler.
1851
	restore_error_handler();
1852
1853
	if ($command_line)
1854
	{
1855
		echo ' Successful.' . "\n";
1856
		flush();
1857
	}
1858
1859
	$_GET['substep'] = 0;
1860
	return true;
1861
}
1862
1863
function upgrade_query($string, $unbuffered = false)
1864
{
1865
	global $db_connection, $db_server, $db_user, $db_passwd, $db_type, $command_line, $upcontext, $upgradeurl, $modSettings;
1866
	global $db_name, $db_unbuffered, $smcFunc;
1867
1868
	// Get the query result - working around some SMF specific security - just this once!
1869
	$modSettings['disableQueryCheck'] = true;
1870
	$db_unbuffered = $unbuffered;
1871
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
1872
	$db_unbuffered = false;
1873
1874
	// Failure?!
1875
	if ($result !== false)
1876
		return $result;
1877
1878
	$db_error_message = $smcFunc['db_error']($db_connection);
1879
	// If MySQL we do something more clever.
1880
	if ($db_type == 'mysql')
1881
	{
1882
		$mysqli_errno = mysqli_errno($db_connection);
1883
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR'));
1884
1885
		// Error numbers:
1886
		//    1016: Can't open file '....MYI'
1887
		//    1050: Table already exists.
1888
		//    1054: Unknown column name.
1889
		//    1060: Duplicate column name.
1890
		//    1061: Duplicate key name.
1891
		//    1062: Duplicate entry for unique key.
1892
		//    1068: Multiple primary keys.
1893
		//    1072: Key column '%s' doesn't exist in table.
1894
		//    1091: Can't drop key, doesn't exist.
1895
		//    1146: Table doesn't exist.
1896
		//    2013: Lost connection to server during query.
1897
1898
		if ($mysqli_errno == 1016)
1899
		{
1900
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
1901
			{
1902
				mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
1903
				$result = mysqli_query($db_connection, $string);
1904
				if ($result !== false)
1905
					return $result;
1906
			}
1907
		}
1908
		elseif ($mysqli_errno == 2013)
1909
		{
1910
			$db_connection = mysqli_connect($db_server, $db_user, $db_passwd);
1911
			mysqli_select_db($db_connection, $db_name);
1912
			if ($db_connection)
1913
			{
1914
				$result = mysqli_query($db_connection, $string);
1915
				if ($result !== false)
1916
					return $result;
1917
			}
1918
		}
1919
		// Duplicate column name... should be okay ;).
1920 View Code Duplication
		elseif (in_array($mysqli_errno, array(1060, 1061, 1068, 1091)))
1921
			return false;
1922
		// Duplicate insert... make sure it's the proper type of query ;).
1923 View Code Duplication
		elseif (in_array($mysqli_errno, array(1054, 1062, 1146)) && $error_query)
1924
			return false;
1925
		// Creating an index on a non-existent column.
1926
		elseif ($mysqli_errno == 1072)
1927
			return false;
1928
		elseif ($mysqli_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
1929
			return false;
1930
	}
1931
	// If a table already exists don't go potty.
1932
	else
1933
	{
1934
		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U')))
1935
		{
1936
			if (strpos($db_error_message, 'exist') !== false)
1937
				return true;
1938
		}
1939
		elseif (strpos(trim($string), 'INSERT ') !== false)
1940
		{
1941
			if (strpos($db_error_message, 'duplicate') !== false)
1942
				return true;
1943
		}
1944
	}
1945
1946
	// Get the query string so we pass everything.
1947
	$query_string = '';
1948
	foreach ($_GET as $k => $v)
1949
		$query_string .= ';' . $k . '=' . $v;
1950
	if (strlen($query_string) != 0)
1951
		$query_string = '?' . substr($query_string, 1);
1952
1953
	if ($command_line)
1954
	{
1955
		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
1956
		die;
1957
	}
1958
1959
	// Bit of a bodge - do we want the error?
1960
	if (!empty($upcontext['return_error']))
1961
	{
1962
		$upcontext['error_message'] = $db_error_message;
1963
		$upcontext['error_string'] = $string;
1964
		return false;
1965
	}
1966
1967
	// Otherwise we have to display this somewhere appropriate if possible.
1968
	$upcontext['forced_error_message'] = '
1969
			<strong>Unsuccessful!</strong><br>
1970
1971
			<div style="margin: 2ex;">
1972
				This query:
1973
				<blockquote><pre>' . nl2br(htmlspecialchars(trim($string))) . ';</pre></blockquote>
1974
1975
				Caused the error:
1976
				<blockquote>' . nl2br(htmlspecialchars($db_error_message)) . '</blockquote>
1977
			</div>
1978
1979
			<form action="' . $upgradeurl . $query_string . '" method="post">
1980
				<input type="submit" value="Try again" class="button_submit">
1981
			</form>
1982
		</div>';
1983
1984
	upgradeExit();
1985
}
1986
1987
// This performs a table alter, but does it unbuffered so the script can time out professionally.
1988
function protected_alter($change, $substep, $is_test = false)
1989
{
1990
	global $db_prefix, $smcFunc;
1991
1992
	db_extend('packages');
1993
1994
	// Firstly, check whether the current index/column exists.
1995
	$found = false;
1996
	if ($change['type'] === 'column')
1997
	{
1998
		$columns = $smcFunc['db_list_columns']('{db_prefix}' . $change['table'], true);
1999
		foreach ($columns as $column)
2000
		{
2001
			// Found it?
2002
			if ($column['name'] === $change['name'])
2003
			{
2004
				$found |= 1;
2005
				// Do some checks on the data if we have it set.
2006
				if (isset($change['col_type']))
2007
					$found &= $change['col_type'] === $column['type'];
2008
				if (isset($change['null_allowed']))
2009
					$found &= $column['null'] == $change['null_allowed'];
2010
				if (isset($change['default']))
2011
					$found &= $change['default'] === $column['default'];
2012
			}
2013
		}
2014
	}
2015
	elseif ($change['type'] === 'index')
2016
	{
2017
		$request = upgrade_query('
2018
			SHOW INDEX
2019
			FROM ' . $db_prefix . $change['table']);
2020
		if ($request !== false)
2021
		{
2022
			$cur_index = array();
2023
2024
			while ($row = $smcFunc['db_fetch_assoc']($request))
2025
				if ($row['Key_name'] === $change['name'])
2026
					$cur_index[(int) $row['Seq_in_index']] = $row['Column_name'];
2027
2028
			ksort($cur_index, SORT_NUMERIC);
2029
			$found = array_values($cur_index) === $change['target_columns'];
2030
2031
			$smcFunc['db_free_result']($request);
2032
		}
2033
	}
2034
2035
	// If we're trying to add and it's added, we're done.
2036
	if ($found && in_array($change['method'], array('add', 'change')))
2037
		return true;
2038
	// Otherwise if we're removing and it wasn't found we're also done.
2039
	elseif (!$found && in_array($change['method'], array('remove', 'change_remove')))
2040
		return true;
2041
	// Otherwise is it just a test?
2042
	elseif ($is_test)
2043
		return false;
2044
2045
	// Not found it yet? Bummer! How about we see if we're currently doing it?
2046
	$running = false;
2047
	$found = false;
2048
	while (1 == 1)
2049
	{
2050
		$request = upgrade_query('
2051
			SHOW FULL PROCESSLIST');
2052
		while ($row = $smcFunc['db_fetch_assoc']($request))
2053
		{
2054
			if (strpos($row['Info'], 'ALTER TABLE ' . $db_prefix . $change['table']) !== false && strpos($row['Info'], $change['text']) !== false)
2055
				$found = true;
2056
		}
2057
2058
		// Can't find it? Then we need to run it fools!
2059
		if (!$found && !$running)
2060
		{
2061
			$smcFunc['db_free_result']($request);
2062
2063
			$success = upgrade_query('
2064
				ALTER TABLE ' . $db_prefix . $change['table'] . '
2065
				' . $change['text'], true) !== false;
2066
2067
			if (!$success)
2068
				return false;
2069
2070
			// Return
2071
			$running = true;
2072
		}
2073
		// What if we've not found it, but we'd ran it already? Must of completed.
2074
		elseif (!$found)
2075
		{
2076
			$smcFunc['db_free_result']($request);
2077
			return true;
2078
		}
2079
2080
		// Pause execution for a sec or three.
2081
		sleep(3);
2082
2083
		// Can never be too well protected.
2084
		nextSubstep($substep);
2085
	}
2086
2087
	// Protect it.
2088
	nextSubstep($substep);
2089
}
2090
2091
/**
2092
 * Alter a text column definition preserving its character set.
2093
 *
2094
 * @param array $change
2095
 * @param int $substep
2096
 */
2097
function textfield_alter($change, $substep)
2098
{
2099
	global $db_prefix, $smcFunc;
2100
2101
	$request = $smcFunc['db_query']('', '
2102
		SHOW FULL COLUMNS
2103
		FROM {db_prefix}' . $change['table'] . '
2104
		LIKE {string:column}',
2105
		array(
2106
			'column' => $change['column'],
2107
			'db_error_skip' => true,
2108
		)
2109
	);
2110
	if ($smcFunc['db_num_rows']($request) === 0)
2111
		die('Unable to find column ' . $change['column'] . ' inside table ' . $db_prefix . $change['table']);
2112
	$table_row = $smcFunc['db_fetch_assoc']($request);
2113
	$smcFunc['db_free_result']($request);
2114
2115
	// If something of the current column definition is different, fix it.
2116
	$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']);
2117
2118
	// Columns that previously allowed null, need to be converted first.
2119
	$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
2120
2121
	// Get the character set that goes with the collation of the column.
2122 View Code Duplication
	if ($column_fix && !empty($table_row['Collation']))
2123
	{
2124
		$request = $smcFunc['db_query']('', '
2125
			SHOW COLLATION
2126
			LIKE {string:collation}',
2127
			array(
2128
				'collation' => $table_row['Collation'],
2129
				'db_error_skip' => true,
2130
			)
2131
		);
2132
		// No results? Just forget it all together.
2133
		if ($smcFunc['db_num_rows']($request) === 0)
2134
			unset($table_row['Collation']);
2135
		else
2136
			$collation_info = $smcFunc['db_fetch_assoc']($request);
2137
		$smcFunc['db_free_result']($request);
2138
	}
2139
2140
	if ($column_fix)
2141
	{
2142
		// Make sure there are no NULL's left.
2143
		if ($null_fix)
2144
			$smcFunc['db_query']('', '
2145
				UPDATE {db_prefix}' . $change['table'] . '
2146
				SET ' . $change['column'] . ' = {string:default}
2147
				WHERE ' . $change['column'] . ' IS NULL',
2148
				array(
2149
					'default' => isset($change['default']) ? $change['default'] : '',
2150
					'db_error_skip' => true,
2151
				)
2152
			);
2153
2154
		// Do the actual alteration.
2155
		$smcFunc['db_query']('', '
2156
			ALTER TABLE {db_prefix}' . $change['table'] . '
2157
			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}' : ''),
2158
			array(
2159
				'default' => isset($change['default']) ? $change['default'] : '',
2160
				'db_error_skip' => true,
2161
			)
2162
		);
2163
	}
2164
	nextSubstep($substep);
2165
}
2166
2167
// Check if we need to alter this query.
2168
function checkChange(&$change)
2169
{
2170
	global $smcFunc, $db_type, $databases;
2171
	static $database_version, $where_field_support;
2172
2173
	// Attempt to find a database_version.
2174
	if (empty($database_version))
2175
	{
2176
		$database_version = $databases[$db_type]['version_check'];
2177
		$where_field_support = $db_type == 'mysql' && version_compare('5.0', $database_version, '<=');
2178
	}
2179
2180
	// Not a column we need to check on?
2181
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
2182
		return;
2183
2184
	// Break it up you (six|seven).
2185
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
2186
2187
	// Can we support a shortcut method?
2188
	if ($where_field_support)
2189
	{
2190
		// Get the details about this change.
2191
		$request = $smcFunc['db_query']('', '
2192
			SHOW FIELDS
2193
			FROM {db_prefix}{raw:table}
2194
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
2195
			array(
2196
				'table' => $change['table'],
2197
				'old_name' => $temp[1],
2198
				'new_name' => $temp[2],
2199
		));
2200
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2201
		if ($smcFunc['db_num_rows'] != 1)
2202
			return;
2203
2204
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
2205
		$smcFunc['db_free_result']($request);
2206
	}
2207
	else
2208
	{
2209
		// Do this the old fashion, sure method way.
2210
		$request = $smcFunc['db_query']('', '
2211
			SHOW FIELDS
2212
			FROM {db_prefix}{raw:table}',
2213
			array(
2214
				'table' => $change['table'],
2215
		));
2216
		// Mayday!
2217
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2218
		if ($smcFunc['db_num_rows'] == 0)
2219
			return;
2220
2221
		// Oh where, oh where has my little field gone. Oh where can it be...
2222
		while ($row = $smcFunc['db_query']($request))
2223
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
2224
			{
2225
				$current_type = $row['Type'];
2226
				break;
2227
			}
2228
	}
2229
2230
	// If this doesn't match, the column may of been altered for a reason.
2231
	if (trim($current_type) != trim($temp[3]))
2232
		$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...
2233
2234
	// Piece this back together.
2235
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
2236
}
2237
2238
// The next substep.
2239
function nextSubstep($substep)
2240
{
2241
	global $start_time, $timeLimitThreshold, $command_line, $custom_warning;
2242
	global $step_progress, $is_debug, $upcontext;
2243
2244
	if ($_GET['substep'] < $substep)
2245
		$_GET['substep'] = $substep;
2246
2247
	if ($command_line)
2248
	{
2249
		if (time() - $start_time > 1 && empty($is_debug))
2250
		{
2251
			echo '.';
2252
			$start_time = time();
2253
		}
2254
		return;
2255
	}
2256
2257
	@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...
2258
	if (function_exists('apache_reset_timeout'))
2259
		@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...
2260
2261
	if (time() - $start_time <= $timeLimitThreshold)
2262
		return;
2263
2264
	// Do we have some custom step progress stuff?
2265
	if (!empty($step_progress))
2266
	{
2267
		$upcontext['substep_progress'] = 0;
2268
		$upcontext['substep_progress_name'] = $step_progress['name'];
2269
		if ($step_progress['current'] > $step_progress['total'])
2270
			$upcontext['substep_progress'] = 99.9;
2271
		else
2272
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2273
2274
		// Make it nicely rounded.
2275
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2276
	}
2277
2278
	// If this is XML we just exit right away!
2279
	if (isset($_GET['xml']))
2280
		return upgradeExit();
2281
2282
	// We're going to pause after this!
2283
	$upcontext['pause'] = true;
2284
2285
	$upcontext['query_string'] = '';
2286
	foreach ($_GET as $k => $v)
2287
	{
2288
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2289
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2290
	}
2291
2292
	// Custom warning?
2293
	if (!empty($custom_warning))
2294
		$upcontext['custom_warning'] = $custom_warning;
2295
2296
	upgradeExit();
2297
}
2298
2299
function cmdStep0()
2300
{
2301
	global $boarddir, $sourcedir, $modSettings, $start_time, $cachedir, $databases, $db_type, $smcFunc, $upcontext;
2302
	global $is_debug;
2303
	$start_time = time();
2304
2305
	ob_end_clean();
2306
	ob_implicit_flush(true);
2307
	@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...
2308
2309
	if (!isset($_SERVER['argv']))
2310
		$_SERVER['argv'] = array();
2311
	$_GET['maint'] = 1;
2312
2313
	foreach ($_SERVER['argv'] as $i => $arg)
2314
	{
2315
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2316
			$_GET['lang'] = $match[1];
2317
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2318
			continue;
2319
		elseif ($arg == '--no-maintenance')
2320
			$_GET['maint'] = 0;
2321
		elseif ($arg == '--debug')
2322
			$is_debug = true;
2323
		elseif ($arg == '--backup')
2324
			$_POST['backup'] = 1;
2325
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2326
			$_GET['conv'] = 1;
2327
		elseif ($i != 0)
2328
		{
2329
			echo 'SMF Command-line Upgrader
2330
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2331
2332
    --language=LANG         Reset the forum\'s language to LANG.
2333
    --no-maintenance        Don\'t put the forum into maintenance mode.
2334
    --debug                 Output debugging information.
2335
    --backup                Create backups of tables with "backup_" prefix.';
2336
			echo "\n";
2337
			exit;
2338
		}
2339
	}
2340
2341
	if (!php_version_check())
2342
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
2343
	if (!db_version_check())
2344
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2345
2346
	// Do some checks to make sure they have proper privileges
2347
	db_extend('packages');
2348
2349
	// CREATE
2350
	$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');
2351
2352
	// ALTER
2353
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'tinytext', 'null' => false, 'default' => ''));
2354
2355
	// DROP
2356
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2357
2358
	// Sorry... we need CREATE, ALTER and DROP
2359 View Code Duplication
	if (!$create || !$alter || !$drop)
2360
		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);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal The does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
2361
2362
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2363
		&& @file_exists($sourcedir . '/QueryString.php')
2364
		&& @file_exists($sourcedir . '/ManageBoards.php');
2365
	if (!$check && !isset($modSettings['smfVersion']))
2366
		print_error('Error: Some files are missing or out-of-date.', true);
2367
2368
	// Do a quick version spot check.
2369
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
2370
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2371
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2372
		print_error('Error: Some files have not yet been updated properly.');
2373
2374
	// Make sure Settings.php is writable.
2375
	quickFileWritable($boarddir . '/Settings.php');
2376
	if (!is_writable($boarddir . '/Settings.php'))
2377
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2378
2379
	// Make sure Settings_bak.php is writable.
2380
	quickFileWritable($boarddir . '/Settings_bak.php');
2381
	if (!is_writable($boarddir . '/Settings_bak.php'))
2382
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2383
2384
	// Make sure db_last_error.php is writable.
2385
	quickFileWritable($boarddir . '/db_last_error.php');
2386
	if (!is_writable($boarddir . '/db_last_error.php'))
2387
		print_error('Error: Unable to obtain write access to "db_last_error.php".');
2388
2389 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
2390
		print_error('Error: Unable to obtain write access to "agreement.txt".');
2391
	elseif (isset($modSettings['agreement']))
2392
	{
2393
		$fp = fopen($boarddir . '/agreement.txt', 'w');
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $fp. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
2394
		fwrite($fp, $modSettings['agreement']);
2395
		fclose($fp);
2396
	}
2397
2398
	// Make sure Themes is writable.
2399
	quickFileWritable($modSettings['theme_dir']);
2400
2401
	if (!is_writable($modSettings['theme_dir']) && !isset($modSettings['smfVersion']))
2402
		print_error('Error: Unable to obtain write access to "Themes".');
2403
2404
	// Make sure cache directory exists and is writable!
2405
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
2406
	if (!file_exists($cachedir_temp))
2407
		@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...
2408
2409
	// Make sure the cache temp dir is writable.
2410
	quickFileWritable($cachedir_temp);
2411
2412
	if (!is_writable($cachedir_temp))
2413
		print_error('Error: Unable to obtain write access to "cache".', true);
2414
2415
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
2416
		print_error('Error: Unable to find language files!', true);
2417
	else
2418
	{
2419
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
2420
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
2421
2422
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
2423
			print_error('Error: Language files out of date.', true);
2424
		if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
2425
			print_error('Error: Install language is missing for selected language.', true);
2426
2427
		// Otherwise include it!
2428
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
2429
	}
2430
2431
	// Make sure we skip the HTML for login.
2432
	$_POST['upcont'] = true;
2433
	$upcontext['current_step'] = 1;
2434
}
2435
2436
/**
2437
 * Handles converting your database to UTF-8
2438
 */
2439
function ConvertUtf8()
2440
{
2441
	global $upcontext, $db_character_set, $sourcedir, $smcFunc, $modSettings, $language, $db_prefix, $db_type, $command_line, $support_js;
2442
2443
	// Done it already?
2444
	if (!empty($_POST['utf8_done']))
2445
		return true;
2446
2447
	// First make sure they aren't already on UTF-8 before we go anywhere...
2448
	if ($db_type == 'postgresql' || ($db_character_set === 'utf8' && !empty($modSettings['global_character_set']) && $modSettings['global_character_set'] === 'UTF-8'))
2449
	{
2450
		$smcFunc['db_insert']('replace',
2451
			'{db_prefix}settings',
2452
			array('variable' => 'string', 'value' => 'string'),
2453
			array(array('global_character_set', 'UTF-8')),
2454
			array('variable')
2455
		);
2456
2457
		return true;
2458
	}
2459
	else
2460
	{
2461
		$upcontext['page_title'] = 'Converting to UTF8';
2462
		$upcontext['sub_template'] = isset($_GET['xml']) ? 'convert_xml' : 'convert_utf8';
2463
2464
		// The character sets used in SMF's language files with their db equivalent.
2465
		$charsets = array(
2466
			// Armenian
2467
			'armscii8' => 'armscii8',
2468
			// Chinese-traditional.
2469
			'big5' => 'big5',
2470
			// Chinese-simplified.
2471
			'gbk' => 'gbk',
2472
			// West European.
2473
			'ISO-8859-1' => 'latin1',
2474
			// Romanian.
2475
			'ISO-8859-2' => 'latin2',
2476
			// Turkish.
2477
			'ISO-8859-9' => 'latin5',
2478
			// Latvian
2479
			'ISO-8859-13' => 'latin7',
2480
			// West European with Euro sign.
2481
			'ISO-8859-15' => 'latin9',
2482
			// Thai.
2483
			'tis-620' => 'tis620',
2484
			// Persian, Chinese, etc.
2485
			'UTF-8' => 'utf8',
2486
			// Russian.
2487
			'windows-1251' => 'cp1251',
2488
			// Greek.
2489
			'windows-1253' => 'utf8',
2490
			// Hebrew.
2491
			'windows-1255' => 'utf8',
2492
			// Arabic.
2493
			'windows-1256' => 'cp1256',
2494
		);
2495
2496
		// Get a list of character sets supported by your MySQL server.
2497
		$request = $smcFunc['db_query']('', '
2498
			SHOW CHARACTER SET',
2499
			array(
2500
			)
2501
		);
2502
		$db_charsets = array();
2503
		while ($row = $smcFunc['db_fetch_assoc']($request))
2504
			$db_charsets[] = $row['Charset'];
2505
2506
		$smcFunc['db_free_result']($request);
2507
2508
		// Character sets supported by both MySQL and SMF's language files.
2509
		$charsets = array_intersect($charsets, $db_charsets);
2510
2511
		// Use the messages.body column as indicator for the database charset.
2512
		$request = $smcFunc['db_query']('', '
2513
			SHOW FULL COLUMNS
2514
			FROM {db_prefix}messages
2515
			LIKE {string:body_like}',
2516
			array(
2517
				'body_like' => 'body',
2518
			)
2519
		);
2520
		$column_info = $smcFunc['db_fetch_assoc']($request);
2521
		$smcFunc['db_free_result']($request);
2522
2523
		// A collation looks like latin1_swedish. We only need the character set.
2524
		list($upcontext['database_charset']) = explode('_', $column_info['Collation']);
2525
		$upcontext['database_charset'] = in_array($upcontext['database_charset'], $charsets) ? array_search($upcontext['database_charset'], $charsets) : $upcontext['database_charset'];
2526
2527
		// Detect whether a fulltext index is set.
2528
		$request = $smcFunc['db_query']('', '
2529
 			SHOW INDEX
2530
	  	    FROM {db_prefix}messages',
2531
			array(
2532
			)
2533
		);
2534
2535
		$upcontext['dropping_index'] = false;
2536
2537
		// If there's a fulltext index, we need to drop it first...
2538 View Code Duplication
		if ($request !== false || $smcFunc['db_num_rows']($request) != 0)
2539
		{
2540
			while ($row = $smcFunc['db_fetch_assoc']($request))
2541
				if ($row['Column_name'] == 'body' && (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT' || isset($row['Comment']) && $row['Comment'] == 'FULLTEXT'))
2542
					$upcontext['fulltext_index'][] = $row['Key_name'];
2543
			$smcFunc['db_free_result']($request);
2544
2545
			if (isset($upcontext['fulltext_index']))
2546
				$upcontext['fulltext_index'] = array_unique($upcontext['fulltext_index']);
2547
		}
2548
2549
		// Drop it and make a note...
2550
		if (!empty($upcontext['fulltext_index']))
2551
		{
2552
			$upcontext['dropping_index'] = true;
2553
2554
			$smcFunc['db_query']('', '
2555
  			ALTER TABLE {db_prefix}messages
2556
	  		DROP INDEX ' . implode(',
2557
		  	DROP INDEX ', $upcontext['fulltext_index']),
2558
				array(
2559
					'db_error_skip' => true,
2560
				)
2561
			);
2562
2563
			// Update the settings table
2564
			$smcFunc['db_insert']('replace',
2565
				'{db_prefix}settings',
2566
				array('variable' => 'string', 'value' => 'string'),
2567
				array('db_search_index', ''),
2568
				array('variable')
2569
			);
2570
		}
2571
2572
		// Figure out what charset we should be converting from...
2573
		$lang_charsets = array(
2574
			'arabic' => 'windows-1256',
2575
			'armenian_east' => 'armscii-8',
2576
			'armenian_west' => 'armscii-8',
2577
			'azerbaijani_latin' => 'ISO-8859-9',
2578
			'bangla' => 'UTF-8',
2579
			'belarusian' => 'ISO-8859-5',
2580
			'bulgarian' => 'windows-1251',
2581
			'cambodian' => 'UTF-8',
2582
			'chinese_simplified' => 'gbk',
2583
			'chinese_traditional' => 'big5',
2584
			'croation' => 'ISO-8859-2',
2585
			'czech' => 'ISO-8859-2',
2586
			'czech_informal' => 'ISO-8859-2',
2587
			'english_pirate' => 'UTF-8',
2588
			'esperanto' => 'ISO-8859-3',
2589
			'estonian' => 'ISO-8859-15',
2590
			'filipino_tagalog' => 'UTF-8',
2591
			'filipino_vasayan' => 'UTF-8',
2592
			'georgian' => 'UTF-8',
2593
			'greek' => 'ISO-8859-3',
2594
			'hebrew' => 'windows-1255',
2595
			'hungarian' => 'ISO-8859-2',
2596
			'irish' => 'UTF-8',
2597
			'japanese' => 'UTF-8',
2598
			'khmer' => 'UTF-8',
2599
			'korean' => 'UTF-8',
2600
			'kurdish_kurmanji' => 'ISO-8859-9',
2601
			'kurdish_sorani' => 'windows-1256',
2602
			'lao' => 'tis-620',
2603
			'latvian' => 'ISO-8859-13',
2604
			'lithuanian' => 'ISO-8859-4',
2605
			'macedonian' => 'UTF-8',
2606
			'malayalam' => 'UTF-8',
2607
			'mongolian' => 'UTF-8',
2608
			'nepali' => 'UTF-8',
2609
			'persian' => 'UTF-8',
2610
			'polish' => 'ISO-8859-2',
2611
			'romanian' => 'ISO-8859-2',
2612
			'russian' => 'windows-1252',
2613
			'sakha' => 'UTF-8',
2614
			'serbian_cyrillic' => 'ISO-8859-5',
2615
			'serbian_latin' => 'ISO-8859-2',
2616
			'sinhala' => 'UTF-8',
2617
			'slovak' => 'ISO-8859-2',
2618
			'slovenian' => 'ISO-8859-2',
2619
			'telugu' => 'UTF-8',
2620
			'thai' => 'tis-620',
2621
			'turkish' => 'ISO-8859-9',
2622
			'turkmen' => 'ISO-8859-9',
2623
			'ukranian' => 'windows-1251',
2624
			'urdu' => 'UTF-8',
2625
			'uzbek_cyrillic' => 'ISO-8859-5',
2626
			'uzbek_latin' => 'ISO-8859-5',
2627
			'vietnamese' => 'UTF-8',
2628
			'yoruba' => 'UTF-8'
2629
		);
2630
2631
		// Default to ISO-8859-1 unless we detected another supported charset
2632
		$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';
2633
2634
		$upcontext['charset_list'] = array_keys($charsets);
2635
2636
		// Translation table for the character sets not native for MySQL.
2637
		$translation_tables = array(
2638
			'windows-1255' => array(
2639
				'0x81' => '\'\'',		'0x8A' => '\'\'',		'0x8C' => '\'\'',
2640
				'0x8D' => '\'\'',		'0x8E' => '\'\'',		'0x8F' => '\'\'',
2641
				'0x90' => '\'\'',		'0x9A' => '\'\'',		'0x9C' => '\'\'',
2642
				'0x9D' => '\'\'',		'0x9E' => '\'\'',		'0x9F' => '\'\'',
2643
				'0xCA' => '\'\'',		'0xD9' => '\'\'',		'0xDA' => '\'\'',
2644
				'0xDB' => '\'\'',		'0xDC' => '\'\'',		'0xDD' => '\'\'',
2645
				'0xDE' => '\'\'',		'0xDF' => '\'\'',		'0xFB' => '0xD792',
2646
				'0xFC' => '0xE282AC',		'0xFF' => '0xD6B2',		'0xC2' => '0xFF',
2647
				'0x80' => '0xFC',		'0xE2' => '0xFB',		'0xA0' => '0xC2A0',
2648
				'0xA1' => '0xC2A1',		'0xA2' => '0xC2A2',		'0xA3' => '0xC2A3',
2649
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2650
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2651
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2652
				'0xAF' => '0xC2AF',		'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',
2653
				'0xB2' => '0xC2B2',		'0xB3' => '0xC2B3',		'0xB4' => '0xC2B4',
2654
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2655
				'0xB8' => '0xC2B8',		'0xB9' => '0xC2B9',		'0xBB' => '0xC2BB',
2656
				'0xBC' => '0xC2BC',		'0xBD' => '0xC2BD',		'0xBE' => '0xC2BE',
2657
				'0xBF' => '0xC2BF',		'0xD7' => '0xD7B3',		'0xD1' => '0xD781',
2658
				'0xD4' => '0xD7B0',		'0xD5' => '0xD7B1',		'0xD6' => '0xD7B2',
2659
				'0xE0' => '0xD790',		'0xEA' => '0xD79A',		'0xEC' => '0xD79C',
2660
				'0xED' => '0xD79D',		'0xEE' => '0xD79E',		'0xEF' => '0xD79F',
2661
				'0xF0' => '0xD7A0',		'0xF1' => '0xD7A1',		'0xF2' => '0xD7A2',
2662
				'0xF3' => '0xD7A3',		'0xF5' => '0xD7A5',		'0xF6' => '0xD7A6',
2663
				'0xF7' => '0xD7A7',		'0xF8' => '0xD7A8',		'0xF9' => '0xD7A9',
2664
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2665
				'0x86' => '0xE280A0',	'0x87' => '0xE280A1',	'0x89' => '0xE280B0',
2666
				'0x8B' => '0xE280B9',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2667
				'0x95' => '0xE280A2',	'0x97' => '0xE28094',	'0x99' => '0xE284A2',
2668
				'0xC0' => '0xD6B0',		'0xC1' => '0xD6B1',		'0xC3' => '0xD6B3',
2669
				'0xC4' => '0xD6B4',		'0xC5' => '0xD6B5',		'0xC6' => '0xD6B6',
2670
				'0xC7' => '0xD6B7',		'0xC8' => '0xD6B8',		'0xC9' => '0xD6B9',
2671
				'0xCB' => '0xD6BB',		'0xCC' => '0xD6BC',		'0xCD' => '0xD6BD',
2672
				'0xCE' => '0xD6BE',		'0xCF' => '0xD6BF',		'0xD0' => '0xD780',
2673
				'0xD2' => '0xD782',		'0xE3' => '0xD793',		'0xE4' => '0xD794',
2674
				'0xE5' => '0xD795',		'0xE7' => '0xD797',		'0xE9' => '0xD799',
2675
				'0xFD' => '0xE2808E',	'0xFE' => '0xE2808F',	'0x92' => '0xE28099',
2676
				'0x83' => '0xC692',		'0xD3' => '0xD783',		'0x88' => '0xCB86',
2677
				'0x98' => '0xCB9C',		'0x91' => '0xE28098',	'0x96' => '0xE28093',
2678
				'0xBA' => '0xC3B7',		'0x9B' => '0xE280BA',	'0xAA' => '0xC397',
2679
				'0xA4' => '0xE282AA',	'0xE1' => '0xD791',		'0xE6' => '0xD796',
2680
				'0xE8' => '0xD798',		'0xEB' => '0xD79B',		'0xF4' => '0xD7A4',
2681
				'0xFA' => '0xD7AA',
2682
			),
2683
			'windows-1253' => array(
2684
				'0x81' => '\'\'',			'0x88' => '\'\'',			'0x8A' => '\'\'',
2685
				'0x8C' => '\'\'',			'0x8D' => '\'\'',			'0x8E' => '\'\'',
2686
				'0x8F' => '\'\'',			'0x90' => '\'\'',			'0x98' => '\'\'',
2687
				'0x9A' => '\'\'',			'0x9C' => '\'\'',			'0x9D' => '\'\'',
2688
				'0x9E' => '\'\'',			'0x9F' => '\'\'',			'0xAA' => '\'\'',
2689
				'0xD2' => '0xE282AC',			'0xFF' => '0xCE92',			'0xCE' => '0xCE9E',
2690
				'0xB8' => '0xCE88',		'0xBA' => '0xCE8A',		'0xBC' => '0xCE8C',
2691
				'0xBE' => '0xCE8E',		'0xBF' => '0xCE8F',		'0xC0' => '0xCE90',
2692
				'0xC8' => '0xCE98',		'0xCA' => '0xCE9A',		'0xCC' => '0xCE9C',
2693
				'0xCD' => '0xCE9D',		'0xCF' => '0xCE9F',		'0xDA' => '0xCEAA',
2694
				'0xE8' => '0xCEB8',		'0xEA' => '0xCEBA',		'0xEC' => '0xCEBC',
2695
				'0xEE' => '0xCEBE',		'0xEF' => '0xCEBF',		'0xC2' => '0xFF',
2696
				'0xBD' => '0xC2BD',		'0xED' => '0xCEBD',		'0xB2' => '0xC2B2',
2697
				'0xA0' => '0xC2A0',		'0xA3' => '0xC2A3',		'0xA4' => '0xC2A4',
2698
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2699
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2700
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2701
				'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',		'0xB3' => '0xC2B3',
2702
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2703
				'0xBB' => '0xC2BB',		'0xE2' => '0xCEB2',		'0x80' => '0xD2',
2704
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2705
				'0x86' => '0xE280A0',	'0xA1' => '0xCE85',		'0xA2' => '0xCE86',
2706
				'0x87' => '0xE280A1',	'0x89' => '0xE280B0',	'0xB9' => '0xCE89',
2707
				'0x8B' => '0xE280B9',	'0x91' => '0xE28098',	'0x99' => '0xE284A2',
2708
				'0x92' => '0xE28099',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2709
				'0x95' => '0xE280A2',	'0x96' => '0xE28093',	'0x97' => '0xE28094',
2710
				'0x9B' => '0xE280BA',	'0xAF' => '0xE28095',	'0xB4' => '0xCE84',
2711
				'0xC1' => '0xCE91',		'0xC3' => '0xCE93',		'0xC4' => '0xCE94',
2712
				'0xC5' => '0xCE95',		'0xC6' => '0xCE96',		'0x83' => '0xC692',
2713
				'0xC7' => '0xCE97',		'0xC9' => '0xCE99',		'0xCB' => '0xCE9B',
2714
				'0xD0' => '0xCEA0',		'0xD1' => '0xCEA1',		'0xD3' => '0xCEA3',
2715
				'0xD4' => '0xCEA4',		'0xD5' => '0xCEA5',		'0xD6' => '0xCEA6',
2716
				'0xD7' => '0xCEA7',		'0xD8' => '0xCEA8',		'0xD9' => '0xCEA9',
2717
				'0xDB' => '0xCEAB',		'0xDC' => '0xCEAC',		'0xDD' => '0xCEAD',
2718
				'0xDE' => '0xCEAE',		'0xDF' => '0xCEAF',		'0xE0' => '0xCEB0',
2719
				'0xE1' => '0xCEB1',		'0xE3' => '0xCEB3',		'0xE4' => '0xCEB4',
2720
				'0xE5' => '0xCEB5',		'0xE6' => '0xCEB6',		'0xE7' => '0xCEB7',
2721
				'0xE9' => '0xCEB9',		'0xEB' => '0xCEBB',		'0xF0' => '0xCF80',
2722
				'0xF1' => '0xCF81',		'0xF2' => '0xCF82',		'0xF3' => '0xCF83',
2723
				'0xF4' => '0xCF84',		'0xF5' => '0xCF85',		'0xF6' => '0xCF86',
2724
				'0xF7' => '0xCF87',		'0xF8' => '0xCF88',		'0xF9' => '0xCF89',
2725
				'0xFA' => '0xCF8A',		'0xFB' => '0xCF8B',		'0xFC' => '0xCF8C',
2726
				'0xFD' => '0xCF8D',		'0xFE' => '0xCF8E',
2727
			),
2728
		);
2729
2730
		// Make some preparations.
2731
		if (isset($translation_tables[$upcontext['charset_detected']]))
2732
		{
2733
			$replace = '%field%';
2734
2735
			// Build a huge REPLACE statement...
2736
			foreach ($translation_tables[$upcontext['charset_detected']] as $from => $to)
2737
				$replace = 'REPLACE(' . $replace . ', ' . $from . ', ' . $to . ')';
2738
		}
2739
2740
		// Get a list of table names ahead of time... This makes it easier to set our substep and such
2741
		db_extend();
2742
		$queryTables = $smcFunc['db_list_tables'](false, $db_prefix . '%');
2743
2744
		$upcontext['table_count'] = count($queryTables);
2745
2746
		// What ones have we already done?
2747
		foreach ($queryTables as $id => $table)
2748
			if ($id < $_GET['substep'])
2749
				$upcontext['previous_tables'][] = $table;
2750
2751
		$upcontext['cur_table_num'] = $_GET['substep'];
2752
		$upcontext['cur_table_name'] = str_replace($db_prefix, '', $queryTables[$_GET['substep']]);
2753
		$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2754
2755
		// Make sure we're ready & have painted the template before proceeding
2756
		if ($support_js && !isset($_GET['xml'])) {
2757
			$_GET['substep'] = 0;
2758
			return false;
2759
		}
2760
2761
		// We want to start at the first table.
2762
		for ($substep = $_GET['substep'], $n = count($queryTables); $substep < $n; $substep++)
2763
		{
2764
			$table = $queryTables[$substep];
2765
2766
			$getTableStatus = $smcFunc['db_query']('', '
2767
				SHOW TABLE STATUS
2768
				LIKE {string:table_name}',
2769
				array(
2770
					'table_name' => str_replace('_', '\_', $table)
2771
				)
2772
			);
2773
2774
			// Only one row so we can just fetch_assoc and free the result...
2775
			$table_info = $smcFunc['db_fetch_assoc']($getTableStatus);
2776
			$smcFunc['db_free_result']($getTableStatus);
2777
2778
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($queryTables[$substep + 1]) ? $queryTables[$substep + 1] : $queryTables[$substep]));
2779
			$upcontext['cur_table_num'] = $substep + 1;
2780
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2781
2782
			// Do we need to pause?
2783
			nextSubstep($substep);
2784
2785
			// Just to make sure it doesn't time out.
2786
			if (function_exists('apache_reset_timeout'))
2787
				@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...
2788
2789
			$table_charsets = array();
2790
2791
			// Loop through each column.
2792
			$queryColumns = $smcFunc['db_query']('', '
2793
				SHOW FULL COLUMNS
2794
				FROM ' . $table_info['Name'],
2795
				array(
2796
				)
2797
			);
2798
			while ($column_info = $smcFunc['db_fetch_assoc']($queryColumns))
2799
			{
2800
				// Only text'ish columns have a character set and need converting.
2801
				if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false)
2802
				{
2803
					$collation = empty($column_info['Collation']) || $column_info['Collation'] === 'NULL' ? $table_info['Collation'] : $column_info['Collation'];
2804
					if (!empty($collation) && $collation !== 'NULL')
2805
					{
2806
						list($charset) = explode('_', $collation);
2807
2808
						// Build structure of columns to operate on organized by charset; only operate on columns not yet utf8
2809
						if ($charset != 'utf8') {
2810
							if (!isset($table_charsets[$charset]))
2811
								$table_charsets[$charset] = array();
2812
2813
							$table_charsets[$charset][] = $column_info;
2814
						}
2815
					}
2816
				}
2817
			}
2818
			$smcFunc['db_free_result']($queryColumns);
2819
2820
			// Only change the non-utf8 columns identified above
2821
			if (count($table_charsets) > 0)
2822
			{
2823
				$updates_blob = '';
2824
				$updates_text = '';
2825
				foreach ($table_charsets as $charset => $columns)
2826
				{
2827
					if ($charset !== $charsets[$upcontext['charset_detected']])
2828
					{
2829
						foreach ($columns as $column)
2830
						{
2831
							$updates_blob .= '
2832
								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'] . '\'') . ',';
2833
							$updates_text .= '
2834
								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'] . '\'') . ',';
2835
						}
2836
					}
2837
				}
2838
2839
				// Change the columns to binary form.
2840
				$smcFunc['db_query']('', '
2841
					ALTER TABLE {raw:table_name}{raw:updates_blob}',
2842
					array(
2843
						'table_name' => $table_info['Name'],
2844
						'updates_blob' => substr($updates_blob, 0, -1),
2845
					)
2846
				);
2847
2848
				// Convert the character set if MySQL has no native support for it.
2849
				if (isset($translation_tables[$upcontext['charset_detected']]))
2850
				{
2851
					$update = '';
2852
					foreach ($table_charsets as $charset => $columns)
2853
						foreach ($columns as $column)
2854
							$update .= '
2855
								' . $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...
2856
2857
					$smcFunc['db_query']('', '
2858
						UPDATE {raw:table_name}
2859
						SET {raw:updates}',
2860
						array(
2861
							'table_name' => $table_info['Name'],
2862
							'updates' => substr($update, 0, -1),
2863
						)
2864
					);
2865
				}
2866
2867
				// Change the columns back, but with the proper character set.
2868
				$smcFunc['db_query']('', '
2869
					ALTER TABLE {raw:table_name}{raw:updates_text}',
2870
					array(
2871
						'table_name' => $table_info['Name'],
2872
						'updates_text' => substr($updates_text, 0, -1),
2873
					)
2874
				);
2875
			}
2876
2877
			// Now do the actual conversion (if still needed).
2878
			if ($charsets[$upcontext['charset_detected']] !== 'utf8')
2879
			{
2880
				if ($command_line)
2881
					echo 'Converting table ' . $table_info['Name'] . ' to UTF-8...';
2882
2883
				$smcFunc['db_query']('', '
2884
					ALTER TABLE {raw:table_name}
2885
					CONVERT TO CHARACTER SET utf8',
2886
					array(
2887
						'table_name' => $table_info['Name'],
2888
					)
2889
				);
2890
2891
				if ($command_line)
2892
					echo " done.\n";
2893
			}
2894
			// If this is XML to keep it nice for the user do one table at a time anyway!
2895
			if (isset($_GET['xml']) && $upcontext['cur_table_num'] < $upcontext['table_count'])
2896
				return upgradeExit();
2897
		}
2898
2899
		$prev_charset = empty($translation_tables[$upcontext['charset_detected']]) ? $charsets[$upcontext['charset_detected']] : $translation_tables[$upcontext['charset_detected']];
2900
2901
		$smcFunc['db_insert']('replace',
2902
			'{db_prefix}settings',
2903
			array('variable' => 'string', 'value' => 'string'),
2904
			array(array('global_character_set', 'UTF-8'), array('previousCharacterSet', $prev_charset)),
2905
			array('variable')
2906
		);
2907
2908
		// Store it in Settings.php too because it's needed before db connection.
2909
		// Hopefully this works...
2910
		require_once($sourcedir . '/Subs-Admin.php');
2911
		updateSettingsFile(array('db_character_set' => '\'utf8\''));
2912
2913
		// The conversion might have messed up some serialized strings. Fix them!
2914
		$request = $smcFunc['db_query']('', '
2915
			SELECT id_action, extra
2916
			FROM {db_prefix}log_actions
2917
			WHERE action IN ({string:remove}, {string:delete})',
2918
			array(
2919
				'remove' => 'remove',
2920
				'delete' => 'delete',
2921
			)
2922
		);
2923
		while ($row = $smcFunc['db_fetch_assoc']($request))
2924
		{
2925
			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)
2926
				$smcFunc['db_query']('', '
2927
					UPDATE {db_prefix}log_actions
2928
					SET extra = {string:extra}
2929
					WHERE id_action = {int:current_action}',
2930
					array(
2931
						'current_action' => $row['id_action'],
2932
						'extra' => $matches[1] . strlen($matches[3]) . ':"' . $matches[3] . '"' . $matches[4],
2933
					)
2934
				);
2935
		}
2936
		$smcFunc['db_free_result']($request);
2937
2938
		if ($upcontext['dropping_index'] && $command_line)
2939
		{
2940
			echo "\nYour fulltext search index was dropped to facilitate the conversion. You will need to recreate it.";
2941
			flush();
2942
		}
2943
	}
2944
	$_GET['substep'] = 0;
2945
	return false;
2946
}
2947
2948
function serialize_to_json()
2949
{
2950
	global $command_line, $smcFunc, $modSettings, $sourcedir, $upcontext, $support_js;
2951
2952
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'serialize_json_xml' : 'serialize_json';
2953
	// First thing's first - did we already do this?
2954
	if (!empty($modSettings['json_done']))
2955
	{
2956
		if ($command_line)
2957
			return DeleteUpgrade();
2958
		else
2959
			return true;
2960
	}
2961
2962
	// Done it already - js wise?
2963
	if (!empty($_POST['json_done']))
2964
		return true;
2965
2966
	// List of tables affected by this function
2967
	// 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...
2968
	// If 3rd item in array is true, it indicates that col1 could be empty...
2969
	$tables = array(
2970
		'background_tasks' => array('id_task', 'task_data'),
2971
		'log_actions' => array('id_action', 'extra'),
2972
		'log_online' => array('session', 'url'),
2973
		'log_packages' => array('id_install', 'db_changes', 'failed_steps', 'credits'),
2974
		'log_spider_hits' => array('id_hit', 'url'),
2975
		'log_subscribed' => array('id_sublog', 'pending_details'),
2976
		'pm_rules' => array('id_rule', 'criteria', 'actions'),
2977
		'qanda' => array('id_question', 'answers'),
2978
		'subscriptions' => array('id_subscribe', 'cost'),
2979
		'user_alerts' => array('id_alert', 'extra', true),
2980
		'user_drafts' => array('id_draft', 'to_list', true),
2981
		// These last two are a bit different - we'll handle those separately
2982
		'settings' => array(),
2983
		'themes' => array()
2984
	);
2985
2986
	// Set up some context stuff...
2987
	// Because we're not using numeric indices, we need this to figure out the current table name...
2988
	$keys = array_keys($tables);
2989
2990
	$upcontext['page_title'] = 'Converting to JSON';
2991
	$upcontext['table_count'] = count($keys);
2992
	$upcontext['cur_table_num'] = $_GET['substep'];
2993
	$upcontext['cur_table_name'] = isset($keys[$_GET['substep']]) ? $keys[$_GET['substep']] : $keys[0];
2994
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2995
2996 View Code Duplication
	foreach ($keys as $id => $table)
2997
		if ($id < $_GET['substep'])
2998
			$upcontext['previous_tables'][] = $table;
2999
3000
	if ($command_line)
3001
		echo 'Converting data from serialize() to json_encode().';
3002
3003
	if (!$support_js || isset($_GET['xml']))
3004
	{
3005
		// Fix the data in each table
3006
		for ($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
3007
		{
3008
			$upcontext['cur_table_name'] = isset($keys[$substep + 1]) ? $keys[$substep + 1] : $keys[$substep];
3009
			$upcontext['cur_table_num'] = $substep + 1;
3010
3011
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3012
3013
			// Do we need to pause?
3014
			nextSubstep($substep);
3015
3016
			// Initialize a few things...
3017
			$where = '';
3018
			$vars = array();
3019
			$table = $keys[$substep];
3020
			$info = $tables[$table];
3021
3022
			// Now the fun - build our queries and all that fun stuff
3023
			if ($table == 'settings')
3024
			{
3025
				// Now a few settings...
3026
				$serialized_settings = array(
3027
					'attachment_basedirectories',
3028
					'attachmentUploadDir',
3029
					'cal_today_birthday',
3030
					'cal_today_event',
3031
					'cal_today_holiday',
3032
					'displayFields',
3033
					'last_attachments_directory',
3034
					'memberlist_cache',
3035
					'search_custom_index_config',
3036
					'spider_name_cache'
3037
				);
3038
3039
				// Loop through and fix these...
3040
				$new_settings = array();
3041
				if ($command_line)
3042
					echo "\n" . 'Fixing some settings...';
3043
3044
				foreach ($serialized_settings as $var)
3045
				{
3046
					if (isset($modSettings[$var]))
3047
					{
3048
						// Attempt to unserialize the setting
3049
						$temp = @safe_unserialize($modSettings[$var]);
3050
						if (!$temp && $command_line)
3051
							echo "\n - Failed to unserialize the '" . $var . "' setting. Skipping.";
3052
						elseif ($temp !== false)
3053
							$new_settings[$var] = json_encode($temp);
3054
					}
3055
				}
3056
3057
				// Update everything at once
3058
				if (!function_exists('cache_put_data'))
3059
					require_once($sourcedir . '/Load.php');
3060
				updateSettings($new_settings, true);
3061
3062
				if ($command_line)
3063
					echo ' done.';
3064
			}
3065
			elseif ($table == 'themes')
3066
			{
3067
				// Finally, fix the admin prefs. Unfortunately this is stored per theme, but hopefully they only have one theme installed at this point...
3068
				$query = $smcFunc['db_query']('', '
3069
					SELECT id_member, id_theme, value FROM {db_prefix}themes
3070
					WHERE variable = {string:admin_prefs}',
3071
						array(
3072
							'admin_prefs' => 'admin_preferences'
3073
						)
3074
				);
3075
3076
				if ($smcFunc['db_num_rows']($query) != 0)
3077
				{
3078
					while ($row = $smcFunc['db_fetch_assoc']($query))
3079
					{
3080
						$temp = @safe_unserialize($row['value']);
3081
3082
						if ($command_line)
3083
						{
3084
							if ($temp === false)
3085
								echo "\n" . 'Unserialize of admin_preferences for user ' . $row['id_member'] . ' failed. Skipping.';
3086
							else
3087
								echo "\n" . 'Fixing admin preferences...';
3088
						}
3089
3090
						if ($temp !== false)
3091
						{
3092
							$row['value'] = json_encode($temp);
3093
3094
							// Even though we have all values from the table, UPDATE is still faster than REPLACE
3095
							$smcFunc['db_query']('', '
3096
								UPDATE {db_prefix}themes
3097
								SET value = {string:prefs}
3098
								WHERE id_theme = {int:theme}
3099
									AND id_member = {int:member}
3100
									AND variable = {string:admin_prefs}',
3101
								array(
3102
									'prefs' => $row['value'],
3103
									'theme' => $row['id_theme'],
3104
									'member' => $row['id_member'],
3105
									'admin_prefs' => 'admin_preferences'
3106
								)
3107
							);
3108
3109
							if ($command_line)
3110
								echo ' done.';
3111
						}
3112
					}
3113
3114
					$smcFunc['db_free_result']($query);
3115
				}
3116
			}
3117
			else
3118
			{
3119
				// First item is always the key...
3120
				$key = $info[0];
3121
				unset($info[0]);
3122
3123
				// Now we know what columns we have and such...
3124
				if (count($info) == 2 && $info[2] === true)
3125
				{
3126
					$col_select = $info[1];
3127
					$where = ' WHERE ' . $info[1] . ' != {empty}';
3128
				}
3129
				else
3130
				{
3131
					$col_select = implode(', ', $info);
3132
				}
3133
3134
				$query = $smcFunc['db_query']('', '
3135
					SELECT ' . $key . ', ' . $col_select . '
3136
					FROM {db_prefix}' . $table . $where,
3137
					array()
3138
				);
3139
3140
				if ($smcFunc['db_num_rows']($query) != 0)
3141
				{
3142
					if ($command_line)
3143
					{
3144
						echo "\n" . ' +++ Fixing the "' . $table . '" table...';
3145
						flush();
3146
					}
3147
3148
					while ($row = $smcFunc['db_fetch_assoc']($query))
3149
					{
3150
						$update = '';
3151
3152
						// We already know what our key is...
3153
						foreach ($info as $col)
3154
						{
3155
							if ($col !== true && $row[$col] != '')
3156
							{
3157
								$temp = @safe_unserialize($row[$col]);
3158
3159
								if ($temp === false && $command_line)
3160
								{
3161
									echo "\nFailed to unserialize " . $row[$col] . "... Skipping\n";
3162
								}
3163
								else
3164
								{
3165
									$row[$col] = json_encode($temp);
3166
3167
									// Build our SET string and variables array
3168
									$update .= (empty($update) ? '' : ', ') . $col . ' = {string:' . $col . '}';
3169
									$vars[$col] = $row[$col];
3170
								}
3171
							}
3172
						}
3173
3174
						$vars[$key] = $row[$key];
3175
3176
						// In a few cases, we might have empty data, so don't try to update in those situations...
3177
						if (!empty($update))
3178
						{
3179
							$smcFunc['db_query']('', '
3180
								UPDATE {db_prefix}' . $table . '
3181
								SET ' . $update . '
3182
								WHERE ' . $key . ' = {' . ($key == 'session' ? 'string' : 'int') . ':' . $key . '}',
3183
								$vars
3184
							);
3185
						}
3186
					}
3187
3188
					if ($command_line)
3189
						echo ' done.';
3190
3191
					// Free up some memory...
3192
					$smcFunc['db_free_result']($query);
3193
				}
3194
			}
3195
			// If this is XML to keep it nice for the user do one table at a time anyway!
3196
			if (isset($_GET['xml']))
3197
				return upgradeExit();
3198
		}
3199
3200
		if ($command_line)
3201
		{
3202
			echo "\n" . 'Successful.' . "\n";
3203
			flush();
3204
		}
3205
		$upcontext['step_progress'] = 100;
3206
3207
		// Last but not least, insert a dummy setting so we don't have to do this again in the future...
3208
		updateSettings(array('json_done' => true));
3209
3210
		$_GET['substep'] = 0;
3211
		// Make sure we move on!
3212
		if ($command_line)
3213
			return DeleteUpgrade();
3214
3215
		return true;
3216
	}
3217
3218
	// If this fails we just move on to deleting the upgrade anyway...
3219
	$_GET['substep'] = 0;
3220
	return false;
3221
}
3222
3223
/******************************************************************************
3224
******************* Templates are below this point ****************************
3225
******************************************************************************/
3226
3227
// This is what is displayed if there's any chmod to be done. If not it returns nothing...
3228
function template_chmod()
3229
{
3230
	global $upcontext, $txt, $settings;
3231
3232
	// Don't call me twice!
3233
	if (!empty($upcontext['chmod_called']))
3234
		return;
3235
3236
	$upcontext['chmod_called'] = true;
3237
3238
	// Nothing?
3239
	if (empty($upcontext['chmod']['files']) && empty($upcontext['chmod']['ftp_error']))
3240
		return;
3241
3242
	// Was it a problem with Windows?
3243
	if (!empty($upcontext['chmod']['ftp_error']) && $upcontext['chmod']['ftp_error'] == 'total_mess')
3244
	{
3245
		echo '
3246
			<div class="error_message red">
3247
				The following files need to be writable to continue the upgrade. Please ensure the Windows permissions are correctly set to allow this:<br>
3248
				<ul style="margin: 2.5ex; font-family: monospace;">
3249
					<li>' . implode('</li>
3250
					<li>', $upcontext['chmod']['files']) . '</li>
3251
				</ul>
3252
			</div>';
3253
3254
		return false;
3255
	}
3256
3257
	echo '
3258
		<div class="panel">
3259
			<h2>Your FTP connection information</h2>
3260
			<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>
3261
			<script>
3262
				function warning_popup()
3263
				{
3264
					popup = window.open(\'\',\'popup\',\'height=150,width=400,scrollbars=yes\');
3265
					var content = popup.document;
3266
					content.write(\'<!DOCTYPE html>\n\');
3267
					content.write(\'<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>\n\t<head>\n\t\t<meta name="robots" content="noindex">\n\t\t\');
3268
					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\');
3269
					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\');
3270
					content.write(\'<p>', implode('<br>\n\t\t\t', $upcontext['chmod']['files']), '</p>\n\t\t\t\');';
3271
3272
	if (isset($upcontext['systemos']) && $upcontext['systemos'] == 'linux')
3273
		echo '
3274
					content.write(\'<hr>\n\t\t\t\');
3275
					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\');
3276
					content.write(\'<tt># chmod a+w ', implode(' ', $upcontext['chmod']['files']), '</tt>\n\t\t\t\');';
3277
3278
	echo '
3279
					content.write(\'<a href="javascript:self.close();">close</a>\n\t\t</div>\n\t</body>\n</html>\');
3280
					content.close();
3281
				}
3282
			</script>';
3283
3284
	if (!empty($upcontext['chmod']['ftp_error']))
3285
		echo '
3286
			<div class="error_message red">
3287
				The following error was encountered when trying to connect:<br><br>
3288
				<code>', $upcontext['chmod']['ftp_error'], '</code>
3289
			</div>
3290
			<br>';
3291
3292
	if (empty($upcontext['chmod_in_form']))
3293
		echo '
3294
	<form action="', $upcontext['form_url'], '" method="post">';
3295
3296
	echo '
3297
		<table width="520" border="0" align="center" style="margin-bottom: 1ex;">
3298
			<tr>
3299
				<td width="26%" valign="top" class="textbox"><label for="ftp_server">', $txt['ftp_server'], ':</label></td>
3300
				<td>
3301
					<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>
3302
					<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">
3303
					<div class="smalltext block">', $txt['ftp_server_info'], '</div>
3304
				</td>
3305
			</tr><tr>
3306
				<td width="26%" valign="top" class="textbox"><label for="ftp_username">', $txt['ftp_username'], ':</label></td>
3307
				<td>
3308
					<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">
3309
					<div class="smalltext block">', $txt['ftp_username_info'], '</div>
3310
				</td>
3311
			</tr><tr>
3312
				<td width="26%" valign="top" class="textbox"><label for="ftp_password">', $txt['ftp_password'], ':</label></td>
3313
				<td>
3314
					<input type="password" size="50" name="ftp_password" id="ftp_password" style="width: 99%;" class="input_password">
3315
					<div class="smalltext block">', $txt['ftp_password_info'], '</div>
3316
				</td>
3317
			</tr><tr>
3318
				<td width="26%" valign="top" class="textbox"><label for="ftp_path">', $txt['ftp_path'], ':</label></td>
3319
				<td style="padding-bottom: 1ex;">
3320
					<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">
3321
					<div class="smalltext block">', !empty($upcontext['chmod']['path']) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'], '</div>
3322
				</td>
3323
			</tr>
3324
		</table>
3325
3326
		<div class="righttext" style="margin: 1ex;"><input type="submit" value="', $txt['ftp_connect'], '" class="button_submit"></div>
3327
	</div>';
3328
3329
	if (empty($upcontext['chmod_in_form']))
3330
		echo '
3331
	</form>';
3332
}
3333
3334
function template_upgrade_above()
3335
{
3336
	global $modSettings, $txt, $settings, $upcontext, $upgradeurl;
3337
3338
	echo '<!DOCTYPE html>
3339
<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>
3340
	<head>
3341
		<meta charset="', isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8', '">
3342
		<meta name="robots" content="noindex">
3343
		<title>', $txt['upgrade_upgrade_utility'], '</title>
3344
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/index.css?alp21">
3345
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/install.css?alp21">
3346
		', $txt['lang_rtl'] == true ? '<link rel="stylesheet" href="' . $settings['default_theme_url'] . '/css/rtl.css?alp21">' : '', '
3347
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
3348
		<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
3349
		<script>
3350
			var smf_scripturl = \'', $upgradeurl, '\';
3351
			var smf_charset = \'', (empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'UTF-8' : $txt['lang_character_set']) : $modSettings['global_character_set']), '\';
3352
			var startPercent = ', $upcontext['overall_percent'], ';
3353
3354
			// This function dynamically updates the step progress bar - and overall one as required.
3355
			function updateStepProgress(current, max, overall_weight)
3356
			{
3357
				// What out the actual percent.
3358
				var width = parseInt((current / max) * 100);
3359
				if (document.getElementById(\'step_progress\'))
3360
				{
3361
					document.getElementById(\'step_progress\').style.width = width + "%";
3362
					setInnerHTML(document.getElementById(\'step_text\'), width + "%");
3363
				}
3364
				if (overall_weight && document.getElementById(\'overall_progress\'))
3365
				{
3366
					overall_width = parseInt(startPercent + width * (overall_weight / 100));
3367
					document.getElementById(\'overall_progress\').style.width = overall_width + "%";
3368
					setInnerHTML(document.getElementById(\'overall_text\'), overall_width + "%");
3369
				}
3370
			}
3371
		</script>
3372
	</head>
3373
	<body>
3374
	<div id="footerfix">
3375
		<div id="header">
3376
			<h1 class="forumtitle">', $txt['upgrade_upgrade_utility'], '</h1>
3377
			<img id="smflogo" src="', $settings['default_theme_url'], '/images/smflogo.png" alt="Simple Machines Forum" title="Simple Machines Forum">
3378
		</div>
3379
	<div id="wrapper">
3380
		<div id="upper_section">
3381
			<div id="inner_section">
3382
				<div id="inner_wrap">
3383
				</div>
3384
			</div>
3385
		</div>
3386
		<div id="content_section">
3387
		<div id="main_content_section">
3388
			<div id="main_steps">
3389
				<h2>', $txt['upgrade_progress'], '</h2>
3390
				<ul>';
3391
3392 View Code Duplication
	foreach ($upcontext['steps'] as $num => $step)
3393
		echo '
3394
						<li class="', $num < $upcontext['current_step'] ? 'stepdone' : ($num == $upcontext['current_step'] ? 'stepcurrent' : 'stepwaiting'), '">', $txt['upgrade_step'], ' ', $step[0], ': ', $step[1], '</li>';
3395
3396
	echo '
3397
					</ul>
3398
			</div>
3399
3400
			<div id="progress_bar">
3401
				<div id="overall_text">', $upcontext['overall_percent'], '%</div>
3402
				<div id="overall_progress" style="width: ', $upcontext['overall_percent'], '%;">
3403
					<span>', $txt['upgrade_overall_progress'], '</span>
3404
				</div>
3405
			</div>';
3406
3407
	if (isset($upcontext['step_progress']))
3408
		echo '
3409
				<br>
3410
				<br>
3411
				<div id="progress_bar_step">
3412
					<div id="step_text">', $upcontext['step_progress'], '%</div>
3413
					<div id="step_progress" style="width: ', $upcontext['step_progress'], '%;background-color: #ffd000;">
3414
						<span>', $txt['upgrade_step_progress'], '</span>
3415
					</div>
3416
				</div>';
3417
3418
	echo '
3419
				<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>
3420
				<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', ';">
3421
					<div id="substep_text" style="color: #000; position: absolute; margin-left: -5em;">', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : '', '%</div>
3422
					<div id="substep_progress" style="width: ', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%; height: 12pt; z-index: 1; background-color: #eebaf4;">&nbsp;</div>
3423
				</div>';
3424
3425
	// How long have we been running this?
3426
	$elapsed = time() - $upcontext['started'];
3427
	$mins = (int) ($elapsed / 60);
3428
	$seconds = $elapsed - $mins * 60;
3429
	echo '
3430
								<br> <br> <br> <br> <br>
3431
								<div class="smalltext" style="padding: 5px; text-align: center;"><br>', $txt['upgrade_time_elapsed'], ':
3432
									<span id="mins_elapsed">', $mins, '</span> ', $txt['upgrade_time_mins'], ', <span id="secs_elapsed">', $seconds, '</span> ', $txt['upgrade_time_secs'], '.
3433
								</div>';
3434
	echo '
3435
			</div>
3436
			</div>
3437
			<div id="main_screen" class="clear">
3438
				<h2>', $upcontext['page_title'], '</h2>
3439
				<div class="panel">
3440
					<div style="max-height: 360px; overflow: auto;">';
3441
}
3442
3443
function template_upgrade_below()
3444
{
3445
	global $upcontext, $txt;
3446
3447
	if (!empty($upcontext['pause']))
3448
		echo '
3449
								<em>', $txt['upgrade_incomplete'], '.</em><br>
3450
3451
								<h2 style="margin-top: 2ex;">', $txt['upgrade_not_quite_done'], '</h2>
3452
								<h3>
3453
									', $txt['upgrade_paused_overload'], '
3454
								</h3>';
3455
3456
	if (!empty($upcontext['custom_warning']))
3457
		echo '
3458
								<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3459
									<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3460
									<strong style="text-decoration: underline;">', $txt['upgrade_note'], '</strong><br>
3461
									<div style="padding-left: 6ex;">', $upcontext['custom_warning'], '</div>
3462
								</div>';
3463
3464
	echo '
3465
								<div class="righttext" style="margin: 1ex;">';
3466
3467
	if (!empty($upcontext['continue']))
3468
		echo '
3469
									<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '"', $upcontext['continue'] == 2 ? ' disabled' : '', ' class="button_submit">';
3470
	if (!empty($upcontext['skip']))
3471
		echo '
3472
									<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="dontSubmit = true; document.getElementById(\'contbutt\').disabled = \'disabled\'; return true;" class="button_submit">';
3473
3474
	echo '
3475
								</div>
3476
							</form>
3477
						</div>
3478
				</div>
3479
			</div>
3480
			</div>
3481
		</div>
3482
		<div id="footer">
3483
			<ul>
3484
				<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>
3485
			</ul>
3486
		</div>
3487
	</body>
3488
</html>';
3489
3490
	// Are we on a pause?
3491
	if (!empty($upcontext['pause']))
3492
	{
3493
		echo '
3494
		<script>
3495
			window.onload = doAutoSubmit;
3496
			var countdown = 3;
3497
			var dontSubmit = false;
3498
3499
			function doAutoSubmit()
3500
			{
3501
				if (countdown == 0 && !dontSubmit)
3502
					document.upform.submit();
3503
				else if (countdown == -1)
3504
					return;
3505
3506
				document.getElementById(\'contbutt\').value = "', $txt['upgrade_continue'], ' (" + countdown + ")";
3507
				countdown--;
3508
3509
				setTimeout("doAutoSubmit();", 1000);
3510
			}
3511
		</script>';
3512
	}
3513
}
3514
3515
function template_xml_above()
3516
{
3517
	global $upcontext;
3518
3519
	echo '<', '?xml version="1.0" encoding="UTF-8"?', '>
3520
	<smf>';
3521
3522
	if (!empty($upcontext['get_data']))
3523
		foreach ($upcontext['get_data'] as $k => $v)
3524
			echo '
3525
		<get key="', $k, '">', $v, '</get>';
3526
}
3527
3528
function template_xml_below()
3529
{
3530
	echo '
3531
		</smf>';
3532
}
3533
3534
function template_error_message()
3535
{
3536
	global $upcontext;
3537
3538
	echo '
3539
	<div class="error_message red">
3540
		', $upcontext['error_msg'], '
3541
		<br>
3542
		<a href="', $_SERVER['PHP_SELF'], '">Click here to try again.</a>
3543
	</div>';
3544
}
3545
3546
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 (L1949-1998) 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...
3547
{
3548
	global $upcontext, $disable_security, $settings, $txt;
3549
3550
	echo '
3551
		<script src="https://www.simplemachines.org/smf/current-version.js?version=' . SMF_VERSION . '"></script>
3552
			<h3>', sprintf($txt['upgrade_ready_proceed'], SMF_VERSION), '</h3>
3553
	<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">
3554
		<input type="hidden" name="', $upcontext['login_token_var'], '" value="', $upcontext['login_token'], '">
3555
		<div id="version_warning" style="margin: 2ex; padding: 2ex; border: 2px dashed #a92174; color: black; background-color: #fbbbe2; display: none;">
3556
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3557
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3558
			<div style="padding-left: 6ex;">
3559
				', sprintf($txt['upgrade_warning_out_of_date'], SMF_VERSION), '
3560
			</div>
3561
		</div>';
3562
3563
	$upcontext['chmod_in_form'] = true;
3564
	template_chmod();
3565
3566
	// For large, pre 1.1 RC2 forums give them a warning about the possible impact of this upgrade!
3567
	if ($upcontext['is_large_forum'])
3568
		echo '
3569
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3570
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3571
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3572
			<div style="padding-left: 6ex;">
3573
				', $txt['upgrade_warning_lots_data'], '
3574
			</div>
3575
		</div>';
3576
3577
	// A warning message?
3578
	if (!empty($upcontext['warning']))
3579
		echo '
3580
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3581
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3582
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3583
			<div style="padding-left: 6ex;">
3584
				', $upcontext['warning'], '
3585
			</div>
3586
		</div>';
3587
3588
	// Paths are incorrect?
3589
	echo '
3590
		<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">
3591
			<div style="float: left; width: 2ex; font-size: 2em; color: black;">!!</div>
3592
			<strong style="text-decoration: underline;">', $txt['upgrade_critical_error'], '</strong><br>
3593
			<div style="padding-left: 6ex;">
3594
				', $txt['upgrade_error_script_js'], '
3595
			</div>
3596
		</div>';
3597
3598
	// Is there someone already doing this?
3599
	if (!empty($upcontext['user']['id']) && (time() - $upcontext['started'] < 72600 || time() - $upcontext['updated'] < 3600))
3600
	{
3601
		$ago = time() - $upcontext['started'];
3602
		if ($ago < 60)
3603
			$ago = $ago . ' seconds';
3604
		elseif ($ago < 3600)
3605
			$ago = (int) ($ago / 60) . ' minutes';
3606
		else
3607
			$ago = (int) ($ago / 3600) . ' hours';
3608
3609
		$active = time() - $upcontext['updated'];
3610
		if ($active < 60)
3611
			$updated = $active . ' seconds';
3612
		elseif ($active < 3600)
3613
			$updated = (int) ($active / 60) . ' minutes';
3614
		else
3615
			$updated = (int) ($active / 3600) . ' hours';
3616
3617
		echo '
3618
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3619
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3620
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3621
			<div style="padding-left: 6ex;">
3622
				&quot;', $upcontext['user']['name'], '&quot; has been running the upgrade script for the last ', $ago, ' - and was last active ', $updated, ' ago.';
3623
3624
		if ($active < 600)
3625
			echo '
3626
				We recommend that you do not run this script unless you are sure that ', $upcontext['user']['name'], ' has completed their upgrade.';
3627
3628
		if ($active > $upcontext['inactive_timeout'])
3629
			echo '
3630
				<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.';
3631
		else
3632
			echo '
3633
				<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!');
3634
3635
		echo '
3636
			</div>
3637
		</div>';
3638
	}
3639
3640
	echo '
3641
			<strong>Admin Login: ', $disable_security ? '(DISABLED)' : '', '</strong>
3642
			<h3>For security purposes please login with your admin account to proceed with the upgrade.</h3>
3643
			<table>
3644
				<tr valign="top">
3645
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Username:</strong></td>
3646
					<td>
3647
						<input type="text" name="user" value="', !empty($upcontext['username']) ? $upcontext['username'] : '', '"', $disable_security ? ' disabled' : '', ' class="input_text">';
3648
3649
	if (!empty($upcontext['username_incorrect']))
3650
		echo '
3651
						<div class="smalltext" style="color: red;">Username Incorrect</div>';
3652
3653
	echo '
3654
					</td>
3655
				</tr>
3656
				<tr valign="top">
3657
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Password:</strong></td>
3658
					<td>
3659
						<input type="password" name="passwrd" value=""', $disable_security ? ' disabled' : '', ' class="input_password">
3660
						<input type="hidden" name="hash_passwrd" value="">';
3661
3662
	if (!empty($upcontext['password_failed']))
3663
		echo '
3664
						<div class="smalltext" style="color: red;">Password Incorrect</div>';
3665
3666
	echo '
3667
					</td>
3668
				</tr>';
3669
3670
	// Can they continue?
3671
	if (!empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] >= $upcontext['inactive_timeout'] && $upcontext['user']['step'] > 1)
3672
	{
3673
		echo '
3674
				<tr>
3675
					<td colspan="2">
3676
						<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>
3677
					</td>
3678
				</tr>';
3679
	}
3680
3681
	echo '
3682
			</table><br>
3683
			<span class="smalltext">
3684
				<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.
3685
			</span>
3686
			<input type="hidden" name="login_attempt" id="login_attempt" value="1">
3687
			<input type="hidden" name="js_works" id="js_works" value="0">';
3688
3689
	// Say we want the continue button!
3690
	$upcontext['continue'] = !empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] < $upcontext['inactive_timeout'] ? 2 : 1;
3691
3692
	// This defines whether javascript is going to work elsewhere :D
3693
	echo '
3694
		<script>
3695
			if (\'XMLHttpRequest\' in window && document.getElementById(\'js_works\'))
3696
				document.getElementById(\'js_works\').value = 1;
3697
3698
			// Latest version?
3699
			function smfCurrentVersion()
3700
			{
3701
				var smfVer, yourVer;
3702
3703
				if (!(\'smfVersion\' in window))
3704
					return;
3705
3706
				window.smfVersion = window.smfVersion.replace(/SMF\s?/g, \'\');
3707
3708
				smfVer = document.getElementById(\'smfVersion\');
3709
				yourVer = document.getElementById(\'yourVersion\');
3710
3711
				setInnerHTML(smfVer, window.smfVersion);
3712
3713
				var currentVersion = getInnerHTML(yourVer);
3714
				if (currentVersion < window.smfVersion)
3715
					document.getElementById(\'version_warning\').style.display = \'\';
3716
			}
3717
			addLoadEvent(smfCurrentVersion);
3718
3719
			// This checks that the script file even exists!
3720
			if (typeof(smfSelectText) == \'undefined\')
3721
				document.getElementById(\'js_script_missing_error\').style.display = \'\';
3722
3723
		</script>';
3724
}
3725
3726
function template_upgrade_options()
3727
{
3728
	global $upcontext, $modSettings, $db_prefix, $mmessage, $mtitle;
3729
3730
	echo '
3731
			<h3>Before the upgrade gets underway please review the options below - and hit continue when you\'re ready to begin.</h3>
3732
			<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">';
3733
3734
	// Warning message?
3735
	if (!empty($upcontext['upgrade_options_warning']))
3736
		echo '
3737
		<div style="margin: 1ex; padding: 1ex; border: 1px dashed #cc3344; color: black; background-color: #ffe4e9;">
3738
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3739
			<strong style="text-decoration: underline;">Warning!</strong><br>
3740
			<div style="padding-left: 4ex;">
3741
				', $upcontext['upgrade_options_warning'], '
3742
			</div>
3743
		</div>';
3744
3745
	echo '
3746
				<table>
3747
					<tr valign="top">
3748
						<td width="2%">
3749
							<input type="checkbox" name="backup" id="backup" value="1" class="input_check">
3750
						</td>
3751
						<td width="100%">
3752
							<label for="backup">Backup tables in your database with the prefix &quot;backup_' . $db_prefix . '&quot;.</label> (recommended!)
3753
						</td>
3754
					</tr>
3755
					<tr valign="top">
3756
						<td width="2%">
3757
							<input type="checkbox" name="maint" id="maint" value="1" checked class="input_check">
3758
						</td>
3759
						<td width="100%">
3760
							<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>
3761
							<div id="mainmess" style="display: none;">
3762
								<strong class="smalltext">Maintenance Title: </strong><br>
3763
								<input type="text" name="maintitle" size="30" value="', htmlspecialchars($mtitle), '" class="input_text"><br>
3764
								<strong class="smalltext">Maintenance Message: </strong><br>
3765
								<textarea name="mainmessage" rows="3" cols="50">', htmlspecialchars($mmessage), '</textarea>
3766
							</div>
3767
						</td>
3768
					</tr>
3769
					<tr valign="top">
3770
						<td width="2%">
3771
							<input type="checkbox" name="debug" id="debug" value="1" class="input_check">
3772
						</td>
3773
						<td width="100%">
3774
							<label for="debug">Output extra debugging information</label>
3775
						</td>
3776
					</tr>
3777
					<tr valign="top">
3778
						<td width="2%">
3779
							<input type="checkbox" name="empty_error" id="empty_error" value="1" class="input_check">
3780
						</td>
3781
						<td width="100%">
3782
							<label for="empty_error">Empty error log before upgrading</label>
3783
						</td>
3784
					</tr>';
3785
3786
	if (!empty($upcontext['karma_installed']['good']) || !empty($upcontext['karma_installed']['bad']))
3787
		echo '
3788
					<tr valign="top">
3789
						<td width="2%">
3790
							<input type="checkbox" name="delete_karma" id="delete_karma" value="1" class="input_check">
3791
						</td>
3792
						<td width="100%">
3793
							<label for="delete_karma">Delete all karma settings and info from the DB</label>
3794
						</td>
3795
					</tr>';
3796
3797
	echo '
3798
					<tr valign="top">
3799
						<td width="2%">
3800
							<input type="checkbox" name="stats" id="stats" value="1"', empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']) ? '' : ' checked="checked"', ' class="input_check" />
3801
						</td>
3802
						<td width="100%">
3803
							<label for="stat">
3804
								Allow Simple Machines to Collect Basic Stats Monthly.<br>
3805
								<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>
3806
							</label>
3807
						</td>
3808
					</tr>
3809
				</table>
3810
				<input type="hidden" name="upcont" value="1">';
3811
3812
	// We need a normal continue button here!
3813
	$upcontext['continue'] = 1;
3814
}
3815
3816
// Template for the database backup tool/
3817
function template_backup_database()
3818
{
3819
	global $upcontext, $support_js, $is_debug;
3820
3821
	echo '
3822
			<h3>Please wait while a backup is created. For large forums this may take some time!</h3>';
3823
3824
	echo '
3825
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
3826
			<input type="hidden" name="backup_done" id="backup_done" value="0">
3827
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
3828
			<div id="debug_section" style="height: ', ($is_debug ? '115' : '12') , 'px; overflow: auto;">
3829
			<span id="debuginfo"></span>
3830
			</div>';
3831
3832
	// Dont any tables so far?
3833
	if (!empty($upcontext['previous_tables']))
3834
		foreach ($upcontext['previous_tables'] as $table)
3835
			echo '
3836
			<br>Completed Table: &quot;', $table, '&quot;.';
3837
3838
	echo '
3839
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
3840
			<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>';
3841
3842
	// Continue please!
3843
	$upcontext['continue'] = $support_js ? 2 : 1;
3844
3845
	// If javascript allows we want to do this using XML.
3846
	if ($support_js)
3847
	{
3848
		echo '
3849
		<script>
3850
			var lastTable = ', $upcontext['cur_table_num'], ';
3851
			function getNextTables()
3852
			{
3853
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
3854
			}
3855
3856
			// Got an update!
3857
			function onBackupUpdate(oXMLDoc)
3858
			{
3859
				var sCurrentTableName = "";
3860
				var iTableNum = 0;
3861
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
3862
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
3863
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
3864
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
3865
3866
				// Update the page.
3867
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
3868
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
3869
				lastTable = iTableNum;
3870
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
3871
3872
		// If debug flood the screen.
3873
		if ($is_debug)
3874
			echo '
3875
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
3876
3877
				if (document.getElementById(\'debug_section\').scrollHeight)
3878
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
3879
3880
		echo '
3881
				// Get the next update...
3882
				if (iTableNum == ', $upcontext['table_count'], ')
3883
				{
3884
					document.getElementById(\'commess\').style.display = "";
3885
					document.getElementById(\'current_tab_div\').style.display = "none";
3886
					document.getElementById(\'contbutt\').disabled = 0;
3887
					document.getElementById(\'backup_done\').value = 1;
3888
				}
3889
				else
3890
					getNextTables();
3891
			}
3892
			getNextTables();
3893
		//# sourceURL=dynamicScript-bkup.js
3894
		</script>';
3895
	}
3896
}
3897
3898
function template_backup_xml()
3899
{
3900
	global $upcontext;
3901
3902
	echo '
3903
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
3904
}
3905
3906
// Here is the actual "make the changes" template!
3907
function template_database_changes()
3908
{
3909
	global $upcontext, $support_js, $is_debug, $timeLimitThreshold;
3910
3911
	if (empty($is_debug) && !empty($upcontext['upgrade_status']['debug']))
3912
		$is_debug = true;
3913
3914
	echo '
3915
		<h3>Executing database changes</h3>
3916
		<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>';
3917
3918
	echo '
3919
		<form action="', $upcontext['form_url'], '&amp;filecount=', $upcontext['file_count'], '" name="upform" id="upform" method="post">
3920
		<input type="hidden" name="database_done" id="database_done" value="0">';
3921
3922
	// No javascript looks rubbish!
3923
	if (!$support_js)
3924
	{
3925
		foreach ($upcontext['actioned_items'] as $num => $item)
3926
		{
3927
			if ($num != 0)
3928
				echo ' Successful!';
3929
			echo '<br>' . $item;
3930
		}
3931 View Code Duplication
		if (!empty($upcontext['changes_complete']))
3932
		{
3933
			if ($is_debug)
3934
			{
3935
				$active = time() - $upcontext['started'];
3936
				$hours = floor($active / 3600);
3937
				$minutes = intval(($active / 60) % 60);
3938
				$seconds = intval($active % 60);
3939
3940
				$totalTime = '';
3941
				if ($hours > 0)
3942
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
3943
				if ($minutes > 0)
3944
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
3945
				if ($seconds > 0)
3946
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
3947
			}
3948
3949
			if ($is_debug && !empty($totalTime))
3950
				echo ' Successful! Completed in ', $totalTime, '<br><br>';
3951
			else
3952
				echo ' Successful!<br><br>';
3953
3954
			echo '<span id="commess" style="font-weight: bold;">1 Database Updates Complete! Click Continue to Proceed.</span><br>';
3955
		}
3956
	}
3957
	else
3958
	{
3959
		// Tell them how many files we have in total.
3960
		if ($upcontext['file_count'] > 1)
3961
			echo '
3962
		<strong id="info1">Executing upgrade script <span id="file_done">', $upcontext['cur_file_num'], '</span> of ', $upcontext['file_count'], '.</strong>';
3963
3964
		echo '
3965
		<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>
3966
		<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>';
3967
3968 View Code Duplication
		if ($is_debug)
3969
		{
3970
			if ($upcontext['current_debug_item_num'] == $upcontext['debug_items'])
3971
			{
3972
				$active = time() - $upcontext['started'];
3973
				$hours = floor($active / 3600);
3974
				$minutes = intval(($active / 60) % 60);
3975
				$seconds = intval($active % 60);
3976
3977
				$totalTime = '';
3978
				if ($hours > 0)
3979
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
3980
				if ($minutes > 0)
3981
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
3982
				if ($seconds > 0)
3983
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
3984
			}
3985
3986
			echo '
3987
			<br><span id="upgradeCompleted">';
3988
3989
			if (!empty($totalTime))
3990
				echo 'Completed in ', $totalTime, '<br>';
3991
3992
			echo '</span>
3993
			<div id="debug_section" style="height: 59px; overflow: auto;">
3994
			<span id="debuginfo"></span>
3995
			</div>';
3996
		}
3997
	}
3998
3999
	// Place for the XML error message.
4000
	echo '
4001
		<div id="error_block" style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9; display: ', empty($upcontext['error_message']) ? 'none' : '', ';">
4002
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4003
			<strong style="text-decoration: underline;">Error!</strong><br>
4004
			<div style="padding-left: 6ex;" id="error_message">', isset($upcontext['error_message']) ? $upcontext['error_message'] : 'Unknown Error!', '</div>
4005
		</div>';
4006
4007
	// We want to continue at some point!
4008
	$upcontext['continue'] = $support_js ? 2 : 1;
4009
4010
	// If javascript allows we want to do this using XML.
4011
	if ($support_js)
4012
	{
4013
		echo '
4014
		<script>
4015
			var lastItem = ', $upcontext['current_debug_item_num'], ';
4016
			var sLastString = "', strtr($upcontext['current_debug_item_name'], array('"' => '&quot;')), '";
4017
			var iLastSubStepProgress = -1;
4018
			var curFile = ', $upcontext['cur_file_num'], ';
4019
			var totalItems = 0;
4020
			var prevFile = 0;
4021
			var retryCount = 0;
4022
			var testvar = 0;
4023
			var timeOutID = 0;
4024
			var getData = "";
4025
			var debugItems = ', $upcontext['debug_items'], ';';
4026
4027
		if ($is_debug)
4028
			echo '
4029
			var upgradeStartTime = ' . $upcontext['started'] . ';';
4030
4031
		echo '
4032
			function getNextItem()
4033
			{
4034
				// We want to track this...
4035
				if (timeOutID)
4036
					clearTimeout(timeOutID);
4037
				timeOutID = window.setTimeout("retTimeout()", ', $timeLimitThreshold, '000);
4038
4039
				getXMLDocument(\'', $upcontext['form_url'], '&xml&filecount=', $upcontext['file_count'], '&substep=\' + lastItem + getData, onItemUpdate);
4040
			}
4041
4042
			// Got an update!
4043
			function onItemUpdate(oXMLDoc)
4044
			{
4045
				var sItemName = "";
4046
				var sDebugName = "";
4047
				var iItemNum = 0;
4048
				var iSubStepProgress = -1;
4049
				var iDebugNum = 0;
4050
				var bIsComplete = 0;
4051
				getData = "";
4052
4053
				// We\'ve got something - so reset the timeout!
4054
				if (timeOutID)
4055
					clearTimeout(timeOutID);
4056
4057
				// Assume no error at this time...
4058
				document.getElementById("error_block").style.display = "none";
4059
4060
				// Are we getting some duff info?
4061
				if (!oXMLDoc.getElementsByTagName("item")[0])
4062
				{
4063
					// Too many errors?
4064
					if (retryCount > 15)
4065
					{
4066
						document.getElementById("error_block").style.display = "";
4067
						setInnerHTML(document.getElementById("error_message"), "Error retrieving information on step: " + (sDebugName == "" ? sLastString : sDebugName));';
4068
4069
	if ($is_debug)
4070
		echo '
4071
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4072
4073
	echo '
4074
					}
4075
					else
4076
					{
4077
						retryCount++;
4078
						getNextItem();
4079
					}
4080
					return false;
4081
				}
4082
4083
				// Never allow loops.
4084
				if (curFile == prevFile)
4085
				{
4086
					retryCount++;
4087
					if (retryCount > 10)
4088
					{
4089
						document.getElementById("error_block").style.display = "";
4090
						setInnerHTML(document.getElementById("error_message"), "Upgrade script appears to be going into a loop - step: " + sDebugName);';
4091
4092
	if ($is_debug)
4093
		echo '
4094
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4095
4096
	echo '
4097
					}
4098
				}
4099
				retryCount = 0;
4100
4101
				for (var i = 0; i < oXMLDoc.getElementsByTagName("item")[0].childNodes.length; i++)
4102
					sItemName += oXMLDoc.getElementsByTagName("item")[0].childNodes[i].nodeValue;
4103
				for (var i = 0; i < oXMLDoc.getElementsByTagName("debug")[0].childNodes.length; i++)
4104
					sDebugName += oXMLDoc.getElementsByTagName("debug")[0].childNodes[i].nodeValue;
4105
				for (var i = 0; i < oXMLDoc.getElementsByTagName("get").length; i++)
4106
				{
4107
					getData += "&" + oXMLDoc.getElementsByTagName("get")[i].getAttribute("key") + "=";
4108
					for (var j = 0; j < oXMLDoc.getElementsByTagName("get")[i].childNodes.length; j++)
4109
					{
4110
						getData += oXMLDoc.getElementsByTagName("get")[i].childNodes[j].nodeValue;
4111
					}
4112
				}
4113
4114
				iItemNum = oXMLDoc.getElementsByTagName("item")[0].getAttribute("num");
4115
				iDebugNum = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("num"));
4116
				bIsComplete = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("complete"));
4117
				iSubStepProgress = parseFloat(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("percent"));
4118
				sLastString = sDebugName + " (Item: " + iDebugNum + ")";
4119
4120
				curFile = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("num"));
4121
				debugItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("debug_items"));
4122
				totalItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("items"));
4123
4124
				// If we have an error we haven\'t completed!
4125
				if (oXMLDoc.getElementsByTagName("error")[0] && bIsComplete)
4126
					iDebugNum = lastItem;
4127
4128
				// Do we have the additional progress bar?
4129
				if (iSubStepProgress != -1)
4130
				{
4131
					document.getElementById("substep_bar_div").style.display = "";
4132
					document.getElementById("substep_bar_div2").style.display = "";
4133
					document.getElementById("substep_progress").style.width = iSubStepProgress + "%";
4134
					setInnerHTML(document.getElementById("substep_text"), iSubStepProgress + "%");
4135
					setInnerHTML(document.getElementById("substep_bar_div"), sDebugName.replace(/\./g, "") + ":");
4136
				}
4137
				else
4138
				{
4139
					document.getElementById("substep_bar_div").style.display = "none";
4140
					document.getElementById("substep_bar_div2").style.display = "none";
4141
				}
4142
4143
				// Move onto the next item?
4144
				if (bIsComplete)
4145
					lastItem = iDebugNum;
4146
				else
4147
					lastItem = iDebugNum - 1;
4148
4149
				// Are we finished?
4150
				if (bIsComplete && iDebugNum == -1 && curFile >= ', $upcontext['file_count'], ')
4151
				{';
4152
4153
		if ($is_debug)
4154
			echo '
4155
					document.getElementById(\'debug_section\').style.display = "none";
4156
4157
					var upgradeFinishedTime = parseInt(oXMLDoc.getElementsByTagName("curtime")[0].childNodes[0].nodeValue);
4158
					var diffTime = upgradeFinishedTime - upgradeStartTime;
4159
					var diffHours = Math.floor(diffTime / 3600);
4160
					var diffMinutes = parseInt((diffTime / 60) % 60);
4161
					var diffSeconds = parseInt(diffTime % 60);
4162
4163
					var totalTime = "";
4164
					if (diffHours > 0)
4165
						totalTime = totalTime + diffHours + " hour" + (diffHours > 1 ? "s" : "") + " ";
4166
					if (diffMinutes > 0)
4167
						totalTime = totalTime + diffMinutes + " minute" + (diffMinutes > 1 ? "s" : "") + " ";
4168
					if (diffSeconds > 0)
4169
						totalTime = totalTime + diffSeconds + " second" + (diffSeconds > 1 ? "s" : "");
4170
4171
					setInnerHTML(document.getElementById("upgradeCompleted"), "Completed in " + totalTime);';
4172
4173
		echo '
4174
4175
					document.getElementById(\'commess\').style.display = "";
4176
					document.getElementById(\'contbutt\').disabled = 0;
4177
					document.getElementById(\'database_done\').value = 1;';
4178
4179
		if ($upcontext['file_count'] > 1)
4180
			echo '
4181
					document.getElementById(\'info1\').style.display = "none";';
4182
4183
		echo '
4184
					document.getElementById(\'info2\').style.display = "none";
4185
					updateStepProgress(100, 100, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4186
					return true;
4187
				}
4188
				// Was it the last step in the file?
4189
				else if (bIsComplete && iDebugNum == -1)
4190
				{
4191
					lastItem = 0;
4192
					prevFile = curFile;';
4193
4194
		if ($is_debug)
4195
			echo '
4196
					setOuterHTML(document.getElementById(\'debuginfo\'), \'Moving to next script file...done<br><span id="debuginfo"><\' + \'/span>\');';
4197
4198
		echo '
4199
					getNextItem();
4200
					return true;
4201
				}';
4202
4203
		// If debug scroll the screen.
4204
		if ($is_debug)
4205
			echo '
4206
				if (iLastSubStepProgress == -1)
4207
				{
4208
					// Give it consistent dots.
4209
					dots = sDebugName.match(/\./g);
4210
					numDots = dots ? dots.length : 0;
4211
					for (var i = numDots; i < 3; i++)
4212
						sDebugName += ".";
4213
					setOuterHTML(document.getElementById(\'debuginfo\'), sDebugName + \'<span id="debuginfo"><\' + \'/span>\');
4214
				}
4215
				iLastSubStepProgress = iSubStepProgress;
4216
4217
				if (bIsComplete)
4218
					setOuterHTML(document.getElementById(\'debuginfo\'), \'done<br><span id="debuginfo"><\' + \'/span>\');
4219
				else
4220
					setOuterHTML(document.getElementById(\'debuginfo\'), \'...<span id="debuginfo"><\' + \'/span>\');
4221
4222
				if (document.getElementById(\'debug_section\').scrollHeight)
4223
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4224
4225
		echo '
4226
				// Update the page.
4227
				setInnerHTML(document.getElementById(\'item_num\'), iItemNum);
4228
				setInnerHTML(document.getElementById(\'cur_item_name\'), sItemName);';
4229
4230
		if ($upcontext['file_count'] > 1)
4231
		{
4232
			echo '
4233
				setInnerHTML(document.getElementById(\'file_done\'), curFile);
4234
				setInnerHTML(document.getElementById(\'item_count\'), totalItems);';
4235
		}
4236
4237
		echo '
4238
				// Is there an error?
4239
				if (oXMLDoc.getElementsByTagName("error")[0])
4240
				{
4241
					var sErrorMsg = "";
4242
					for (var i = 0; i < oXMLDoc.getElementsByTagName("error")[0].childNodes.length; i++)
4243
						sErrorMsg += oXMLDoc.getElementsByTagName("error")[0].childNodes[i].nodeValue;
4244
					document.getElementById("error_block").style.display = "";
4245
					setInnerHTML(document.getElementById("error_message"), sErrorMsg);
4246
					return false;
4247
				}
4248
4249
				// Get the progress bar right.
4250
				barTotal = debugItems * ', $upcontext['file_count'], ';
4251
				barDone = (debugItems * (curFile - 1)) + lastItem;
4252
4253
				updateStepProgress(barDone, barTotal, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4254
4255
				// Finally - update the time here as it shows the server is responding!
4256
				curTime = new Date();
4257
				iElapsed = (curTime.getTime() / 1000 - ', $upcontext['started'], ');
4258
				mins = parseInt(iElapsed / 60);
4259
				secs = parseInt(iElapsed - mins * 60);
4260
				setInnerHTML(document.getElementById("mins_elapsed"), mins);
4261
				setInnerHTML(document.getElementById("secs_elapsed"), secs);
4262
4263
				getNextItem();
4264
				return true;
4265
			}
4266
4267
			// What if we timeout?!
4268
			function retTimeout(attemptAgain)
4269
			{
4270
				// Oh noes...
4271
				if (!attemptAgain)
4272
				{
4273
					document.getElementById("error_block").style.display = "";
4274
					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");
4275
				}
4276
				else
4277
				{
4278
					document.getElementById("error_block").style.display = "none";
4279
					getNextItem();
4280
				}
4281
			}';
4282
4283
		// Start things off assuming we've not errored.
4284
		if (empty($upcontext['error_message']))
4285
			echo '
4286
			getNextItem();';
4287
4288
		echo '
4289
		//# sourceURL=dynamicScript-dbch.js
4290
		</script>';
4291
	}
4292
	return;
4293
}
4294
4295
function template_database_xml()
4296
{
4297
	global $is_debug, $upcontext;
4298
4299
	echo '
4300
	<file num="', $upcontext['cur_file_num'], '" items="', $upcontext['total_items'], '" debug_items="', $upcontext['debug_items'], '">', $upcontext['cur_file_name'], '</file>
4301
	<item num="', $upcontext['current_item_num'], '">', $upcontext['current_item_name'], '</item>
4302
	<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>';
4303
4304
	if (!empty($upcontext['error_message']))
4305
		echo '
4306
	<error>', $upcontext['error_message'], '</error>';
4307
4308
	if (!empty($upcontext['error_string']))
4309
		echo '
4310
	<sql>', $upcontext['error_string'], '</sql>';
4311
4312
	if ($is_debug)
4313
		echo '
4314
	<curtime>', time(), '</curtime>';
4315
}
4316
4317
// Template for the UTF-8 conversion step. Basically a copy of the backup stuff with slight modifications....
4318
function template_convert_utf8()
4319
{
4320
	global $upcontext, $support_js, $is_debug;
4321
4322
	echo '
4323
			<h3>Please wait while your database is converted to UTF-8. For large forums this may take some time!</h3>';
4324
4325
	echo '
4326
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4327
			<input type="hidden" name="utf8_done" id="utf8_done" value="0">
4328
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
4329
			<div id="debug_section" style="height: ', ($is_debug ? '97' : '12') , 'px; overflow: auto;">
4330
			<span id="debuginfo"></span>
4331
			</div>';
4332
4333
	// Done any tables so far?
4334
	if (!empty($upcontext['previous_tables']))
4335
		foreach ($upcontext['previous_tables'] as $table)
4336
			echo '
4337
			<br>Completed Table: &quot;', $table, '&quot;.';
4338
4339
	echo '
4340
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>';
4341
4342
	// If we dropped their index, let's let them know
4343
	if ($upcontext['dropping_index'])
4344
		echo '
4345
				<br><span id="indexmsg" style="font-weight: bold; font-style: italic; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">Please note that your fulltext index was dropped to facilitate the conversion and will need to be recreated in the admin area after the upgrade is complete.</span>';
4346
4347
	// Completion notification
4348
	echo '
4349
			<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>';
4350
4351
	// Continue please!
4352
	$upcontext['continue'] = $support_js ? 2 : 1;
4353
4354
	// If javascript allows we want to do this using XML.
4355
	if ($support_js)
4356
	{
4357
		echo '
4358
		<script>
4359
			var lastTable = ', $upcontext['cur_table_num'], ';
4360
			function getNextTables()
4361
			{
4362
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onConversionUpdate);
4363
			}
4364
4365
			// Got an update!
4366
			function onConversionUpdate(oXMLDoc)
4367
			{
4368
				var sCurrentTableName = "";
4369
				var iTableNum = 0;
4370
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4371
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4372
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4373
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4374
4375
				// Update the page.
4376
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4377
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4378
				lastTable = iTableNum;
4379
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4380
4381
		// If debug flood the screen.
4382
		if ($is_debug)
4383
			echo '
4384
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4385
4386
				if (document.getElementById(\'debug_section\').scrollHeight)
4387
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4388
4389
		echo '
4390
				// Get the next update...
4391
				if (iTableNum == ', $upcontext['table_count'], ')
4392
				{
4393
					document.getElementById(\'commess\').style.display = "";
4394
					if (document.getElementById(\'indexmsg\') != null) {
4395
						document.getElementById(\'indexmsg\').style.display = "";
4396
					}
4397
					document.getElementById(\'current_tab_div\').style.display = "none";
4398
					document.getElementById(\'contbutt\').disabled = 0;
4399
					document.getElementById(\'utf8_done\').value = 1;
4400
				}
4401
				else
4402
					getNextTables();
4403
			}
4404
			getNextTables();
4405
		//# sourceURL=dynamicScript-conv.js
4406
		</script>';
4407
	}
4408
}
4409
4410
function template_convert_xml()
4411
{
4412
	global $upcontext;
4413
4414
	echo '
4415
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4416
}
4417
4418
// Template for the database backup tool/
4419
function template_serialize_json()
4420
{
4421
	global $upcontext, $support_js, $is_debug;
4422
4423
	echo '
4424
			<h3>Converting data from serialize to JSON...</h3>';
4425
4426
	echo '
4427
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4428
			<input type="hidden" name="json_done" id="json_done" value="0">
4429
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
4430
			<div id="debug_section" style="height: ', ($is_debug ? '115' : '12') , 'px; overflow: auto;">
4431
			<span id="debuginfo"></span>
4432
			</div>';
4433
4434
	// Dont any tables so far?
4435
	if (!empty($upcontext['previous_tables']))
4436
		foreach ($upcontext['previous_tables'] as $table)
4437
			echo '
4438
			<br>Completed Table: &quot;', $table, '&quot;.';
4439
4440
	echo '
4441
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
4442
			<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>';
4443
4444
	// Try to make sure substep was reset.
4445
	if ($upcontext['cur_table_num'] == $upcontext['table_count'])
4446
		echo '
4447
			<input type="hidden" name="substep" id="substep" value="0">';
4448
4449
	// Continue please!
4450
	$upcontext['continue'] = $support_js ? 2 : 1;
4451
4452
	// If javascript allows we want to do this using XML.
4453
	if ($support_js)
4454
	{
4455
		echo '
4456
		<script>
4457
			var lastTable = ', $upcontext['cur_table_num'], ';
4458
			function getNextTables()
4459
			{
4460
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4461
			}
4462
4463
			// Got an update!
4464
			function onBackupUpdate(oXMLDoc)
4465
			{
4466
				var sCurrentTableName = "";
4467
				var iTableNum = 0;
4468
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4469
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4470
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4471
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4472
4473
				// Update the page.
4474
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4475
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4476
				lastTable = iTableNum;
4477
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4478
4479
		// If debug flood the screen.
4480
		if ($is_debug)
4481
			echo '
4482
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4483
4484
				if (document.getElementById(\'debug_section\').scrollHeight)
4485
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4486
4487
		echo '
4488
				// Get the next update...
4489
				if (iTableNum == ', $upcontext['table_count'], ')
4490
				{
4491
					document.getElementById(\'commess\').style.display = "";
4492
					document.getElementById(\'current_tab_div\').style.display = "none";
4493
					document.getElementById(\'contbutt\').disabled = 0;
4494
					document.getElementById(\'json_done\').value = 1;
4495
				}
4496
				else
4497
					getNextTables();
4498
			}
4499
			getNextTables();
4500
		//# sourceURL=dynamicScript-json.js
4501
		</script>';
4502
	}
4503
}
4504
4505
function template_serialize_json_xml()
4506
{
4507
	global $upcontext;
4508
4509
	echo '
4510
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4511
}
4512
4513
function template_upgrade_complete()
4514
{
4515
	global $upcontext, $upgradeurl, $settings, $boardurl, $is_debug;
4516
4517
	echo '
4518
	<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>
4519
	<form action="', $boardurl, '/index.php">';
4520
4521
	if (!empty($upcontext['can_delete_script']))
4522
		echo '
4523
			<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>
4524
			<script>
4525
				function doTheDelete(theCheck)
4526
				{
4527
					var theImage = document.getElementById ? document.getElementById("delete_upgrader") : document.all.delete_upgrader;
4528
4529
					theImage.src = "', $upgradeurl, '?delete=1&ts_" + (new Date().getTime());
4530
					theCheck.disabled = true;
4531
				}
4532
			</script>
4533
			<img src="', $settings['default_theme_url'], '/images/blank.png" alt="" id="delete_upgrader"><br>';
4534
4535
	$active = time() - $upcontext['started'];
4536
	$hours = floor($active / 3600);
4537
	$minutes = intval(($active / 60) % 60);
4538
	$seconds = intval($active % 60);
4539
4540
	if ($is_debug)
4541
	{
4542
		$totalTime = '';
4543
		if ($hours > 0)
4544
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
4545
		if ($minutes > 0)
4546
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
4547
		if ($seconds > 0)
4548
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
4549
	}
4550
4551
	if ($is_debug && !empty($totalTime))
4552
		echo '<br> Upgrade completed in ', $totalTime, '<br><br>';
4553
4554
	echo '<br>
4555
			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>
4556
			<br>
4557
			Best of luck,<br>
4558
			Simple Machines';
4559
}
4560
4561
/**
4562
 * Convert MySQL (var)char ip col to binary
4563
 *
4564
 * @param string $targetTable The table to perform the operation on
4565
 * @param string $oldCol The old column to gather data from
4566
 * @param string $newCol The new column to put data in
4567
 * @param int $limit The amount of entries to handle at once.
4568
 * @param int $setSize The amount of entries after which to update the database.
4569
 *
4570
 * newCol needs to be a varbinary(16) null able field
4571
 * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
4572
 */
4573
function MySQLConvertOldIp($targetTable, $oldCol, $newCol, $limit = 50000, $setSize = 100)
4574
{
4575
	global $smcFunc, $step_progress;
4576
4577
	$current_substep = $_GET['substep'];
4578
4579
	if (empty($_GET['a']))
4580
		$_GET['a'] = 0;
4581
	$step_progress['name'] = 'Converting ips';
4582
	$step_progress['current'] = $_GET['a'];
4583
4584
	// Skip this if we don't have the column
4585
	$request = $smcFunc['db_query']('', '
4586
		SHOW FIELDS
4587
		FROM {db_prefix}{raw:table}
4588
		WHERE Field = {string:name}',
4589
		array(
4590
			'table' => $targetTable,
4591
			'name' => $oldCol,
4592
	));
4593
	if ($smcFunc['db_num_rows']($request) !== 1)
4594
	{
4595
		$smcFunc['db_free_result']($request);
4596
		return;
4597
	}
4598
	$smcFunc['db_free_result']($request);
4599
4600
	//mysql default max length is 1mb https://dev.mysql.com/doc/refman/5.1/en/packet-too-large.html
4601
	$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...
4602
4603
	$is_done = false;
4604
	while (!$is_done)
4605
	{
4606
		// Keep looping at the current step.
4607
		nextSubStep($current_substep);
4608
4609
		$arIp = array();
4610
4611
		$request = $smcFunc['db_query']('', '
4612
			SELECT DISTINCT {raw:old_col}
4613
			FROM {db_prefix}{raw:table_name}
4614
			WHERE {raw:new_col} IS NULL
4615
			LIMIT {int:limit}',
4616
			array(
4617
				'old_col' => $oldCol,
4618
				'new_col' => $newCol,
4619
				'table_name' => $targetTable,
4620
				'empty' => '',
4621
				'limit' => $limit,
4622
		));
4623
		while ($row = $smcFunc['db_fetch_assoc']($request))
4624
			$arIp[] = $row[$oldCol];
4625
		$smcFunc['db_free_result']($request);
4626
4627
		// Special case, null ip could keep us in a loop.
4628
		if (is_null($arIp[0]))
4629
			unset($arIp[0]);
4630
4631
		if (empty($arIp))
4632
			$is_done = true;
4633
4634
		$updates = array();
4635
		$cases = array();
4636
		$count = count($arIp);
4637
		for ($i = 0; $i < $count; $i++)
4638
		{
4639
			$arIp[$i] = trim($arIp[$i]);
4640
4641
			if (empty($arIp[$i]))
4642
				continue;
4643
4644
			$updates['ip' . $i] = $arIp[$i];
4645
			$cases[$arIp[$i]] = 'WHEN ' . $oldCol . ' = {string:ip' . $i . '} THEN {inet:ip' . $i . '}';
4646
4647
			if ($setSize > 0 && $i % $setSize === 0)
4648
			{
4649
				if (count($updates) == 1)
4650
					continue;
4651
4652
				$updates['whereSet'] = array_values($updates);
4653
				$smcFunc['db_query']('', '
4654
					UPDATE {db_prefix}' . $targetTable . '
4655
					SET ' . $newCol . ' = CASE ' .
4656
					implode('
4657
						', $cases) . '
4658
						ELSE NULL
4659
					END
4660
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4661
					$updates
4662
				);
4663
4664
				$updates = array();
4665
				$cases = array();
4666
			}
4667
		}
4668
4669
		// Incase some extras made it through.
4670
		if (!empty($updates))
4671
		{
4672
			if (count($updates) == 1)
4673
			{
4674
				foreach ($updates as $key => $ip)
4675
				{
4676
					$smcFunc['db_query']('', '
4677
						UPDATE {db_prefix}' . $targetTable . '
4678
						SET ' . $newCol . ' = {inet:ip}
4679
						WHERE ' . $oldCol . ' = {string:ip}',
4680
						array(
4681
							'ip' => $ip
4682
					));
4683
				}
4684
			}
4685
			else
4686
			{
4687
				$updates['whereSet'] = array_values($updates);
4688
				$smcFunc['db_query']('', '
4689
					UPDATE {db_prefix}' . $targetTable . '
4690
					SET ' . $newCol . ' = CASE ' .
4691
					implode('
4692
						', $cases) . '
4693
						ELSE NULL
4694
					END
4695
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4696
					$updates
4697
				);
4698
			}
4699
		}
4700
		else
4701
			$is_done = true;
4702
4703
		$_GET['a'] += $limit;
4704
		$step_progress['current'] = $_GET['a'];
4705
	}
4706
4707
	unset($_GET['a']);
4708
}
4709
4710
/**
4711
 * Get the column info. This is basically the same as smf_db_list_columns but we get 1 column, force detail and other checks.
4712
 *
4713
 * @param string $targetTable The table to perform the operation on
4714
 * @param string $column The column we are looking for.
4715
 *
4716
 * @return array Info on the table.
4717
 */
4718
function upgradeGetColumnInfo($targetTable, $column)
4719
{
4720
 	global $smcFunc;
4721
4722
 	// This should already be here, but be safe.
4723
 	db_extend('packages');
4724
4725
 	$columns = $smcFunc['db_list_columns']($targetTable, true);
4726
4727
	if (isset($columns[$column]))
4728
		return $columns[$column];
4729
	else
4730
		return null;
4731
}
4732
4733
?>