Completed
Push — release-2.1 ( 6f6d35...abeae7 )
by Mathias
08:46
created

upgrade.php ➔ template_upgrade_below()   C

Complexity

Conditions 7
Paths 32

Size

Total Lines 71
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 22
nc 32
nop 0
dl 0
loc 71
rs 6.7968
c 0
b 0
f 0

How to fix   Long Method   

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.2',
41
		'version_check' => '$version = pg_version(); return $version[\'client\'];',
42
		'always_has_db' => true,
43
	),
44
);
45
46
/**
47
 * The maximum time a single substep may take, in seconds.
48
 * @var int
49
 */
50
$timeLimitThreshold = 30;
51
52
/**
53
 * The current path to the upgrade.php file.
54
 * @var string
55
 */
56
$upgrade_path = dirname(__FILE__);
57
58
/**
59
 * The URL of the current page.
60
 * @var string
61
 */
62
$upgradeurl = $_SERVER['PHP_SELF'];
63
64
/**
65
 * Flag to disable the required administrator login.
66
 * @var bool
67
 */
68
$disable_security = false;
69
70
/**
71
 * The amount of seconds allowed between logins.
72
 * If the first user to login is inactive for this amount of seconds, a second login is allowed.
73
 * @var int
74
 */
75
$upcontext['inactive_timeout'] = 10;
76
77
// The helper is crucial. Include it first thing.
78
if (!file_exists($upgrade_path . '/upgrade-helper.php'))
79
    die('upgrade-helper.php not found where it was expected: ' . $upgrade_path . '/upgrade-helper.php! Make sure you have uploaded ALL files from the upgrade package. The upgrader cannot continue.');
80
81
require_once($upgrade_path . '/upgrade-helper.php');
82
83
// All the steps in detail.
84
// Number,Name,Function,Progress Weight.
85
$upcontext['steps'] = array(
86
	0 => array(1, 'Login', 'WelcomeLogin', 2),
87
	1 => array(2, 'Upgrade Options', 'UpgradeOptions', 2),
88
	2 => array(3, 'Backup', 'BackupDatabase', 10),
89
	3 => array(4, 'Database Changes', 'DatabaseChanges', 50),
90
	4 => array(5, 'Convert to UTF-8', 'ConvertUtf8', 20),
91
	5 => array(6, 'Convert serialized strings to JSON', 'serialize_to_json', 10),
92
	6 => array(7, 'Delete Upgrade.php', 'DeleteUpgrade', 1),
93
);
94
// Just to remember which one has files in it.
95
$upcontext['database_step'] = 3;
96
@set_time_limit(600);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

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

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

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

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

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

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

Loading history...
135
		$upcontext['user']['updated'] = 0;
136
137
	$upcontext['started'] = $upcontext['user']['started'];
138
	$upcontext['updated'] = $upcontext['user']['updated'];
139
140
	$is_debug = !empty($upcontext['user']['debug']) ? true : false;
141
}
142
143
// Nothing sensible?
144
if (empty($upcontext['updated']))
145
{
146
	$upcontext['started'] = time();
147
	$upcontext['updated'] = 0;
148
	$upcontext['user'] = array(
149
		'id' => 0,
150
		'name' => 'Guest',
151
		'pass' => 0,
152
		'started' => $upcontext['started'],
153
		'updated' => $upcontext['updated'],
154
	);
155
}
156
157
// Load up some essential data...
158
loadEssentialData();
159
160
// Are we going to be mimic'ing SSI at this point?
161
if (isset($_GET['ssi']))
162
{
163
	require_once($sourcedir . '/Errors.php');
164
	require_once($sourcedir . '/Logging.php');
165
	require_once($sourcedir . '/Load.php');
166
	require_once($sourcedir . '/Security.php');
167
	require_once($sourcedir . '/Subs-Package.php');
168
169
	// SMF isn't started up properly, but loadUserSettings calls our cookies.
170 View Code Duplication
	if (!isset($smcFunc['json_encode']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
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))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
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'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
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))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
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__));
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)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

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

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

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

Loading history...
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)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
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'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
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');
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);
964 View Code Duplication
			if ($fp)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
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);
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']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
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 $boardurl reflects https, set force_ssl
1070
	if (!function_exists('cache_put_data'))
1071
		require_once($sourcedir . '/Load.php');
1072
	if (stripos($boardurl, 'https://') !== false)
1073
		updateSettings(array('force_ssl' => '2'));
1074
1075
	// If we're overriding the language follow it through.
1076 View Code Duplication
	if (isset($_GET['lang']) && file_exists($modSettings['theme_dir'] . '/languages/index.' . $_GET['lang'] . '.php'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1077
		$changes['language'] = '\'' . $_GET['lang'] . '\'';
1078
1079
	if (!empty($_POST['maint']))
1080
	{
1081
		$changes['maintenance'] = '2';
1082
		// Remember what it was...
1083
		$upcontext['user']['main'] = $maintenance;
1084
1085
		if (!empty($_POST['maintitle']))
1086
		{
1087
			$changes['mtitle'] = '\'' . addslashes($_POST['maintitle']) . '\'';
1088
			$changes['mmessage'] = '\'' . addslashes($_POST['mainmessage']) . '\'';
1089
		}
1090
		else
1091
		{
1092
			$changes['mtitle'] = '\'Upgrading the forum...\'';
1093
			$changes['mmessage'] = '\'Don\\\'t worry, we will be back shortly with an updated forum.  It will only be a minute ;).\'';
1094
		}
1095
	}
1096
1097
	if ($command_line)
1098
		echo ' * Updating Settings.php...';
1099
1100
	// Fix some old paths.
1101 View Code Duplication
	if (substr($boarddir, 0, 1) == '.')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
1546
	{
1547
		$smcFunc['db_insert']('ignore',
1548
			$db_prefix . 'themes',
1549
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
1550
			$themeData,
1551
			array('id_member', 'id_theme', 'variable')
1552
		);
1553
	}
1554
}
1555
1556
// This function only works with MySQL but that's fine as it is only used for v1.0.
1557
function convertSettingstoOptions()
1558
{
1559
	global $modSettings, $smcFunc;
1560
1561
	// Format: new_setting -> old_setting_name.
1562
	$values = array(
1563
		'calendar_start_day' => 'cal_startmonday',
1564
		'view_newest_first' => 'viewNewestFirst',
1565
		'view_newest_pm_first' => 'viewNewestFirst',
1566
	);
1567
1568
	foreach ($values as $variable => $value)
1569
	{
1570
		if (empty($modSettings[$value[0]]))
1571
			continue;
1572
1573
		$smcFunc['db_query']('', '
1574
			INSERT IGNORE INTO {db_prefix}themes
1575
				(id_member, id_theme, variable, value)
1576
			SELECT id_member, 1, {string:variable}, {string:value}
1577
			FROM {db_prefix}members',
1578
			array(
1579
				'variable' => $variable,
1580
				'value' => $modSettings[$value[0]],
1581
				'db_error_skip' => true,
1582
			)
1583
		);
1584
1585
		$smcFunc['db_query']('', '
1586
			INSERT IGNORE INTO {db_prefix}themes
1587
				(id_member, id_theme, variable, value)
1588
			VALUES (-1, 1, {string:variable}, {string:value})',
1589
			array(
1590
				'variable' => $variable,
1591
				'value' => $modSettings[$value[0]],
1592
				'db_error_skip' => true,
1593
			)
1594
		);
1595
	}
1596
}
1597
1598
function php_version_check()
1599
{
1600
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
1601
}
1602
1603
function db_version_check()
1604
{
1605
	global $db_type, $databases;
1606
1607
	$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...
1608
	$curver = preg_replace('~\-.+?$~', '', $curver);
1609
1610
	return version_compare($databases[$db_type]['version'], $curver, '<=');
1611
}
1612
1613
function fixRelativePath($path)
1614
{
1615
	global $install_path;
1616
1617
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
1618
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
1619
}
1620
1621
function parse_sql($filename)
1622
{
1623
	global $db_prefix, $db_collation, $boarddir, $boardurl, $command_line, $file_steps, $step_progress, $custom_warning;
1624
	global $upcontext, $support_js, $is_debug, $db_type, $db_character_set;
1625
1626
/*
1627
	Failure allowed on:
1628
		- INSERT INTO but not INSERT IGNORE INTO.
1629
		- UPDATE IGNORE but not UPDATE.
1630
		- ALTER TABLE and ALTER IGNORE TABLE.
1631
		- DROP TABLE.
1632
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
1633
1634
	If a comment...
1635
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
1636
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
1637
		- is only ---#, it is "done." and then a break - only shown in debug.
1638
		- begins with ---{ it is a code block terminating at ---}.
1639
1640
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
1641
1642
	Replaces the following variables:
1643
		- {$boarddir}
1644
		- {$boardurl}
1645
		- {$db_prefix}
1646
		- {$db_collation}
1647
*/
1648
1649
	// May want to use extended functionality.
1650
	db_extend();
1651
	db_extend('packages');
1652
1653
	// Our custom error handler - does nothing but does stop public errors from XML!
1654
	set_error_handler(
1655
		function ($errno, $errstr, $errfile, $errline) use ($support_js)
1656
		{
1657
			if ($support_js)
1658
				return true;
1659
			else
1660
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
1661
		}
1662
	);
1663
1664
	// If we're on MySQL, set {db_collation}; this approach is used throughout upgrade_2-0_mysql.php to set new tables to utf8
1665
	// Note it is expected to be in the format: ENGINE=MyISAM{$db_collation};
1666
	if ($db_type == 'mysql')
1667
		$db_collation = ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1668
	else
1669
		$db_collation = '';
1670
1671
	$endl = $command_line ? "\n" : '<br>' . "\n";
1672
1673
	$lines = file($filename);
1674
1675
	$current_type = 'sql';
1676
	$current_data = '';
1677
	$substep = 0;
1678
	$last_step = '';
1679
1680
	// Make sure all newly created tables will have the proper characters set; this approach is used throughout upgrade_2-1_mysql.php
1681
	if (isset($db_character_set) && $db_character_set === 'utf8')
1682
		$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
1683
1684
	// Count the total number of steps within this file - for progress.
1685
	$file_steps = substr_count(implode('', $lines), '---#');
1686
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
1687
	$upcontext['debug_items'] = $file_steps;
1688
	$upcontext['current_item_num'] = 0;
1689
	$upcontext['current_item_name'] = '';
1690
	$upcontext['current_debug_item_num'] = 0;
1691
	$upcontext['current_debug_item_name'] = '';
1692
	// This array keeps a record of what we've done in case java is dead...
1693
	$upcontext['actioned_items'] = array();
1694
1695
	$done_something = false;
1696
1697
	foreach ($lines as $line_number => $line)
1698
	{
1699
		$do_current = $substep >= $_GET['substep'];
1700
1701
		// Get rid of any comments in the beginning of the line...
1702
		if (substr(trim($line), 0, 2) === '/*')
1703
			$line = preg_replace('~/\*.+?\*/~', '', $line);
1704
1705
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
1706
		if ($is_debug && !$support_js && $command_line)
1707
			flush();
1708
1709
		if (trim($line) === '')
1710
			continue;
1711
1712
		if (trim(substr($line, 0, 3)) === '---')
1713
		{
1714
			$type = substr($line, 3, 1);
1715
1716
			// An error??
1717
			if (trim($current_data) != '' && $type !== '}')
1718
			{
1719
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
1720
				if ($command_line)
1721
					echo $upcontext['error_message'];
1722
			}
1723
1724
			if ($type == ' ')
1725
			{
1726
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
1727
				{
1728
					echo ' Successful.', $endl;
1729
					flush();
1730
				}
1731
1732
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
1733
				$upcontext['current_item_num']++;
1734
				$upcontext['current_item_name'] = $last_step;
1735
1736
				if ($do_current)
1737
				{
1738
					$upcontext['actioned_items'][] = $last_step;
1739
					if ($command_line)
1740
						echo ' * ';
1741
				}
1742
			}
1743
			elseif ($type == '#')
1744
			{
1745
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
1746
1747
				$upcontext['current_debug_item_num']++;
1748
				if (trim($line) != '---#')
1749
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
1750
1751
				// Have we already done something?
1752
				if (isset($_GET['xml']) && $done_something)
1753
				{
1754
					restore_error_handler();
1755
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
1756
				}
1757
1758
				if ($do_current)
1759
				{
1760
					if (trim($line) == '---#' && $command_line)
1761
						echo ' done.', $endl;
1762
					elseif ($command_line)
1763
						echo ' +++ ', rtrim(substr($line, 4));
1764
					elseif (trim($line) != '---#')
1765
					{
1766
						if ($is_debug)
1767
							$upcontext['actioned_items'][] = htmlspecialchars(rtrim(substr($line, 4)));
1768
					}
1769
				}
1770
1771
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
1772
				{
1773
					if ($command_line)
1774
						echo ' * ';
1775
					else
1776
						$upcontext['actioned_items'][] = $last_step;
1777
				}
1778
1779
				// Small step - only if we're actually doing stuff.
1780
				if ($do_current)
1781
					nextSubstep(++$substep);
1782
				else
1783
					$substep++;
1784
			}
1785
			elseif ($type == '{')
1786
				$current_type = 'code';
1787
			elseif ($type == '}')
1788
			{
1789
				$current_type = 'sql';
1790
1791
				if (!$do_current)
1792
				{
1793
					$current_data = '';
1794
					continue;
1795
				}
1796
1797
				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...
1798
				{
1799
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
1800
					if ($command_line)
1801
						echo $upcontext['error_message'];
1802
				}
1803
1804
				// Done with code!
1805
				$current_data = '';
1806
				$done_something = true;
1807
			}
1808
1809
			continue;
1810
		}
1811
1812
		$current_data .= $line;
1813
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
1814
		{
1815
			if ((!$support_js || isset($_GET['xml'])))
1816
			{
1817
				if (!$do_current)
1818
				{
1819
					$current_data = '';
1820
					continue;
1821
				}
1822
1823
				$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));
1824
1825
				upgrade_query($current_data);
1826
1827
				// @todo This will be how it kinda does it once mysql all stripped out - needed for postgre (etc).
1828
				/*
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...
1829
				$result = $smcFunc['db_query']('', $current_data, false, false);
1830
				// Went wrong?
1831
				if (!$result)
1832
				{
1833
					// Bit of a bodge - do we want the error?
1834
					if (!empty($upcontext['return_error']))
1835
					{
1836
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
1837
						return false;
1838
					}
1839
				}*/
1840
				$done_something = true;
1841
			}
1842
			$current_data = '';
1843
		}
1844
		// If this is xml based and we're just getting the item name then that's grand.
1845
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
1846
		{
1847
			restore_error_handler();
1848
			return false;
1849
		}
1850
1851
		// Clean up by cleaning any step info.
1852
		$step_progress = array();
1853
		$custom_warning = '';
1854
	}
1855
1856
	// Put back the error handler.
1857
	restore_error_handler();
1858
1859
	if ($command_line)
1860
	{
1861
		echo ' Successful.' . "\n";
1862
		flush();
1863
	}
1864
1865
	$_GET['substep'] = 0;
1866
	return true;
1867
}
1868
1869
function upgrade_query($string, $unbuffered = false)
1870
{
1871
	global $db_connection, $db_server, $db_user, $db_passwd, $db_type, $command_line, $upcontext, $upgradeurl, $modSettings;
1872
	global $db_name, $db_unbuffered, $smcFunc;
1873
1874
	// Get the query result - working around some SMF specific security - just this once!
1875
	$modSettings['disableQueryCheck'] = true;
1876
	$db_unbuffered = $unbuffered;
1877
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
1878
	$db_unbuffered = false;
1879
1880
	// Failure?!
1881
	if ($result !== false)
1882
		return $result;
1883
1884
	$db_error_message = $smcFunc['db_error']($db_connection);
1885
	// If MySQL we do something more clever.
1886
	if ($db_type == 'mysql')
1887
	{
1888
		$mysqli_errno = mysqli_errno($db_connection);
1889
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR'));
1890
1891
		// Error numbers:
1892
		//    1016: Can't open file '....MYI'
1893
		//    1050: Table already exists.
1894
		//    1054: Unknown column name.
1895
		//    1060: Duplicate column name.
1896
		//    1061: Duplicate key name.
1897
		//    1062: Duplicate entry for unique key.
1898
		//    1068: Multiple primary keys.
1899
		//    1072: Key column '%s' doesn't exist in table.
1900
		//    1091: Can't drop key, doesn't exist.
1901
		//    1146: Table doesn't exist.
1902
		//    2013: Lost connection to server during query.
1903
1904
		if ($mysqli_errno == 1016)
1905
		{
1906
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
1907
			{
1908
				mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
1909
				$result = mysqli_query($db_connection, $string);
1910
				if ($result !== false)
1911
					return $result;
1912
			}
1913
		}
1914
		elseif ($mysqli_errno == 2013)
1915
		{
1916
			$db_connection = mysqli_connect($db_server, $db_user, $db_passwd);
1917
			mysqli_select_db($db_connection, $db_name);
1918
			if ($db_connection)
1919
			{
1920
				$result = mysqli_query($db_connection, $string);
1921
				if ($result !== false)
1922
					return $result;
1923
			}
1924
		}
1925
		// Duplicate column name... should be okay ;).
1926 View Code Duplication
		elseif (in_array($mysqli_errno, array(1060, 1061, 1068, 1091)))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

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

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

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

Loading history...
2129
	{
2130
		$request = $smcFunc['db_query']('', '
2131
			SHOW COLLATION
2132
			LIKE {string:collation}',
2133
			array(
2134
				'collation' => $table_row['Collation'],
2135
				'db_error_skip' => true,
2136
			)
2137
		);
2138
		// No results? Just forget it all together.
2139
		if ($smcFunc['db_num_rows']($request) === 0)
2140
			unset($table_row['Collation']);
2141
		else
2142
			$collation_info = $smcFunc['db_fetch_assoc']($request);
2143
		$smcFunc['db_free_result']($request);
2144
	}
2145
2146
	if ($column_fix)
2147
	{
2148
		// Make sure there are no NULL's left.
2149
		if ($null_fix)
2150
			$smcFunc['db_query']('', '
2151
				UPDATE {db_prefix}' . $change['table'] . '
2152
				SET ' . $change['column'] . ' = {string:default}
2153
				WHERE ' . $change['column'] . ' IS NULL',
2154
				array(
2155
					'default' => isset($change['default']) ? $change['default'] : '',
2156
					'db_error_skip' => true,
2157
				)
2158
			);
2159
2160
		// Do the actual alteration.
2161
		$smcFunc['db_query']('', '
2162
			ALTER TABLE {db_prefix}' . $change['table'] . '
2163
			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}' : ''),
2164
			array(
2165
				'default' => isset($change['default']) ? $change['default'] : '',
2166
				'db_error_skip' => true,
2167
			)
2168
		);
2169
	}
2170
	nextSubstep($substep);
2171
}
2172
2173
// Check if we need to alter this query.
2174
function checkChange(&$change)
2175
{
2176
	global $smcFunc, $db_type, $databases;
2177
	static $database_version, $where_field_support;
2178
2179
	// Attempt to find a database_version.
2180
	if (empty($database_version))
2181
	{
2182
		$database_version = $databases[$db_type]['version_check'];
2183
		$where_field_support = $db_type == 'mysql' && version_compare('5.0', $database_version, '<=');
2184
	}
2185
2186
	// Not a column we need to check on?
2187
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
2188
		return;
2189
2190
	// Break it up you (six|seven).
2191
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
2192
2193
	// Can we support a shortcut method?
2194
	if ($where_field_support)
2195
	{
2196
		// Get the details about this change.
2197
		$request = $smcFunc['db_query']('', '
2198
			SHOW FIELDS
2199
			FROM {db_prefix}{raw:table}
2200
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
2201
			array(
2202
				'table' => $change['table'],
2203
				'old_name' => $temp[1],
2204
				'new_name' => $temp[2],
2205
		));
2206
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2207
		if ($smcFunc['db_num_rows'] != 1)
2208
			return;
2209
2210
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
2211
		$smcFunc['db_free_result']($request);
2212
	}
2213
	else
2214
	{
2215
		// Do this the old fashion, sure method way.
2216
		$request = $smcFunc['db_query']('', '
2217
			SHOW FIELDS
2218
			FROM {db_prefix}{raw:table}',
2219
			array(
2220
				'table' => $change['table'],
2221
		));
2222
		// Mayday!
2223
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2224
		if ($smcFunc['db_num_rows'] == 0)
2225
			return;
2226
2227
		// Oh where, oh where has my little field gone. Oh where can it be...
2228
		while ($row = $smcFunc['db_query']($request))
2229
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
2230
			{
2231
				$current_type = $row['Type'];
2232
				break;
2233
			}
2234
	}
2235
2236
	// If this doesn't match, the column may of been altered for a reason.
2237
	if (trim($current_type) != trim($temp[3]))
2238
		$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...
2239
2240
	// Piece this back together.
2241
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
2242
}
2243
2244
// The next substep.
2245
function nextSubstep($substep)
2246
{
2247
	global $start_time, $timeLimitThreshold, $command_line, $custom_warning;
2248
	global $step_progress, $is_debug, $upcontext;
2249
2250
	if ($_GET['substep'] < $substep)
2251
		$_GET['substep'] = $substep;
2252
2253
	if ($command_line)
2254
	{
2255
		if (time() - $start_time > 1 && empty($is_debug))
2256
		{
2257
			echo '.';
2258
			$start_time = time();
2259
		}
2260
		return;
2261
	}
2262
2263
	@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...
2264
	if (function_exists('apache_reset_timeout'))
2265
		@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...
2266
2267
	if (time() - $start_time <= $timeLimitThreshold)
2268
		return;
2269
2270
	// Do we have some custom step progress stuff?
2271
	if (!empty($step_progress))
2272
	{
2273
		$upcontext['substep_progress'] = 0;
2274
		$upcontext['substep_progress_name'] = $step_progress['name'];
2275
		if ($step_progress['current'] > $step_progress['total'])
2276
			$upcontext['substep_progress'] = 99.9;
2277
		else
2278
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2279
2280
		// Make it nicely rounded.
2281
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2282
	}
2283
2284
	// If this is XML we just exit right away!
2285
	if (isset($_GET['xml']))
2286
		return upgradeExit();
2287
2288
	// We're going to pause after this!
2289
	$upcontext['pause'] = true;
2290
2291
	$upcontext['query_string'] = '';
2292
	foreach ($_GET as $k => $v)
2293
	{
2294
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2295
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2296
	}
2297
2298
	// Custom warning?
2299
	if (!empty($custom_warning))
2300
		$upcontext['custom_warning'] = $custom_warning;
2301
2302
	upgradeExit();
2303
}
2304
2305
function cmdStep0()
2306
{
2307
	global $boarddir, $sourcedir, $modSettings, $start_time, $cachedir, $databases, $db_type, $smcFunc, $upcontext;
2308
	global $is_debug;
2309
	$start_time = time();
2310
2311
	ob_end_clean();
2312
	ob_implicit_flush(true);
2313
	@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...
2314
2315
	if (!isset($_SERVER['argv']))
2316
		$_SERVER['argv'] = array();
2317
	$_GET['maint'] = 1;
2318
2319
	foreach ($_SERVER['argv'] as $i => $arg)
2320
	{
2321
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2322
			$_GET['lang'] = $match[1];
2323
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2324
			continue;
2325
		elseif ($arg == '--no-maintenance')
2326
			$_GET['maint'] = 0;
2327
		elseif ($arg == '--debug')
2328
			$is_debug = true;
2329
		elseif ($arg == '--backup')
2330
			$_POST['backup'] = 1;
2331
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2332
			$_GET['conv'] = 1;
2333
		elseif ($i != 0)
2334
		{
2335
			echo 'SMF Command-line Upgrader
2336
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2337
2338
    --language=LANG         Reset the forum\'s language to LANG.
2339
    --no-maintenance        Don\'t put the forum into maintenance mode.
2340
    --debug                 Output debugging information.
2341
    --backup                Create backups of tables with "backup_" prefix.';
2342
			echo "\n";
2343
			exit;
2344
		}
2345
	}
2346
2347
	if (!php_version_check())
2348
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
2349
	if (!db_version_check())
2350
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2351
2352
	// Do some checks to make sure they have proper privileges
2353
	db_extend('packages');
2354
2355
	// CREATE
2356
	$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');
2357
2358
	// ALTER
2359
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'tinytext', 'null' => false, 'default' => ''));
2360
2361
	// DROP
2362
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2363
2364
	// Sorry... we need CREATE, ALTER and DROP
2365 View Code Duplication
	if (!$create || !$alter || !$drop)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2366
		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);
2367
2368
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2369
		&& @file_exists($sourcedir . '/QueryString.php')
2370
		&& @file_exists($sourcedir . '/ManageBoards.php');
2371
	if (!$check && !isset($modSettings['smfVersion']))
2372
		print_error('Error: Some files are missing or out-of-date.', true);
2373
2374
	// Do a quick version spot check.
2375
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
2376
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2377
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2378
		print_error('Error: Some files have not yet been updated properly.');
2379
2380
	// Make sure Settings.php is writable.
2381
	quickFileWritable($boarddir . '/Settings.php');
2382
	if (!is_writable($boarddir . '/Settings.php'))
2383
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2384
2385
	// Make sure Settings_bak.php is writable.
2386
	quickFileWritable($boarddir . '/Settings_bak.php');
2387
	if (!is_writable($boarddir . '/Settings_bak.php'))
2388
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2389
2390
	// Make sure db_last_error.php is writable.
2391
	quickFileWritable($boarddir . '/db_last_error.php');
2392
	if (!is_writable($boarddir . '/db_last_error.php'))
2393
		print_error('Error: Unable to obtain write access to "db_last_error.php".');
2394
2395 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

Loading history...
2545
		{
2546
			while ($row = $smcFunc['db_fetch_assoc']($request))
2547
				if ($row['Column_name'] == 'body' && (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT' || isset($row['Comment']) && $row['Comment'] == 'FULLTEXT'))
2548
					$upcontext['fulltext_index'][] = $row['Key_name'];
2549
			$smcFunc['db_free_result']($request);
2550
2551
			if (isset($upcontext['fulltext_index']))
2552
				$upcontext['fulltext_index'] = array_unique($upcontext['fulltext_index']);
2553
		}
2554
2555
		// Drop it and make a note...
2556
		if (!empty($upcontext['fulltext_index']))
2557
		{
2558
			$upcontext['dropping_index'] = true;
2559
2560
			$smcFunc['db_query']('', '
2561
  			ALTER TABLE {db_prefix}messages
2562
	  		DROP INDEX ' . implode(',
2563
		  	DROP INDEX ', $upcontext['fulltext_index']),
2564
				array(
2565
					'db_error_skip' => true,
2566
				)
2567
			);
2568
2569
			// Update the settings table
2570
			$smcFunc['db_insert']('replace',
2571
				'{db_prefix}settings',
2572
				array('variable' => 'string', 'value' => 'string'),
2573
				array('db_search_index', ''),
2574
				array('variable')
2575
			);
2576
		}
2577
2578
		// Figure out what charset we should be converting from...
2579
		$lang_charsets = array(
2580
			'arabic' => 'windows-1256',
2581
			'armenian_east' => 'armscii-8',
2582
			'armenian_west' => 'armscii-8',
2583
			'azerbaijani_latin' => 'ISO-8859-9',
2584
			'bangla' => 'UTF-8',
2585
			'belarusian' => 'ISO-8859-5',
2586
			'bulgarian' => 'windows-1251',
2587
			'cambodian' => 'UTF-8',
2588
			'chinese_simplified' => 'gbk',
2589
			'chinese_traditional' => 'big5',
2590
			'croation' => 'ISO-8859-2',
2591
			'czech' => 'ISO-8859-2',
2592
			'czech_informal' => 'ISO-8859-2',
2593
			'english_pirate' => 'UTF-8',
2594
			'esperanto' => 'ISO-8859-3',
2595
			'estonian' => 'ISO-8859-15',
2596
			'filipino_tagalog' => 'UTF-8',
2597
			'filipino_vasayan' => 'UTF-8',
2598
			'georgian' => 'UTF-8',
2599
			'greek' => 'ISO-8859-3',
2600
			'hebrew' => 'windows-1255',
2601
			'hungarian' => 'ISO-8859-2',
2602
			'irish' => 'UTF-8',
2603
			'japanese' => 'UTF-8',
2604
			'khmer' => 'UTF-8',
2605
			'korean' => 'UTF-8',
2606
			'kurdish_kurmanji' => 'ISO-8859-9',
2607
			'kurdish_sorani' => 'windows-1256',
2608
			'lao' => 'tis-620',
2609
			'latvian' => 'ISO-8859-13',
2610
			'lithuanian' => 'ISO-8859-4',
2611
			'macedonian' => 'UTF-8',
2612
			'malayalam' => 'UTF-8',
2613
			'mongolian' => 'UTF-8',
2614
			'nepali' => 'UTF-8',
2615
			'persian' => 'UTF-8',
2616
			'polish' => 'ISO-8859-2',
2617
			'romanian' => 'ISO-8859-2',
2618
			'russian' => 'windows-1252',
2619
			'sakha' => 'UTF-8',
2620
			'serbian_cyrillic' => 'ISO-8859-5',
2621
			'serbian_latin' => 'ISO-8859-2',
2622
			'sinhala' => 'UTF-8',
2623
			'slovak' => 'ISO-8859-2',
2624
			'slovenian' => 'ISO-8859-2',
2625
			'telugu' => 'UTF-8',
2626
			'thai' => 'tis-620',
2627
			'turkish' => 'ISO-8859-9',
2628
			'turkmen' => 'ISO-8859-9',
2629
			'ukranian' => 'windows-1251',
2630
			'urdu' => 'UTF-8',
2631
			'uzbek_cyrillic' => 'ISO-8859-5',
2632
			'uzbek_latin' => 'ISO-8859-5',
2633
			'vietnamese' => 'UTF-8',
2634
			'yoruba' => 'UTF-8'
2635
		);
2636
2637
		// Default to ISO-8859-1 unless we detected another supported charset
2638
		$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';
2639
2640
		$upcontext['charset_list'] = array_keys($charsets);
2641
2642
		// Translation table for the character sets not native for MySQL.
2643
		$translation_tables = array(
2644
			'windows-1255' => array(
2645
				'0x81' => '\'\'',		'0x8A' => '\'\'',		'0x8C' => '\'\'',
2646
				'0x8D' => '\'\'',		'0x8E' => '\'\'',		'0x8F' => '\'\'',
2647
				'0x90' => '\'\'',		'0x9A' => '\'\'',		'0x9C' => '\'\'',
2648
				'0x9D' => '\'\'',		'0x9E' => '\'\'',		'0x9F' => '\'\'',
2649
				'0xCA' => '\'\'',		'0xD9' => '\'\'',		'0xDA' => '\'\'',
2650
				'0xDB' => '\'\'',		'0xDC' => '\'\'',		'0xDD' => '\'\'',
2651
				'0xDE' => '\'\'',		'0xDF' => '\'\'',		'0xFB' => '0xD792',
2652
				'0xFC' => '0xE282AC',		'0xFF' => '0xD6B2',		'0xC2' => '0xFF',
2653
				'0x80' => '0xFC',		'0xE2' => '0xFB',		'0xA0' => '0xC2A0',
2654
				'0xA1' => '0xC2A1',		'0xA2' => '0xC2A2',		'0xA3' => '0xC2A3',
2655
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2656
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2657
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2658
				'0xAF' => '0xC2AF',		'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',
2659
				'0xB2' => '0xC2B2',		'0xB3' => '0xC2B3',		'0xB4' => '0xC2B4',
2660
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2661
				'0xB8' => '0xC2B8',		'0xB9' => '0xC2B9',		'0xBB' => '0xC2BB',
2662
				'0xBC' => '0xC2BC',		'0xBD' => '0xC2BD',		'0xBE' => '0xC2BE',
2663
				'0xBF' => '0xC2BF',		'0xD7' => '0xD7B3',		'0xD1' => '0xD781',
2664
				'0xD4' => '0xD7B0',		'0xD5' => '0xD7B1',		'0xD6' => '0xD7B2',
2665
				'0xE0' => '0xD790',		'0xEA' => '0xD79A',		'0xEC' => '0xD79C',
2666
				'0xED' => '0xD79D',		'0xEE' => '0xD79E',		'0xEF' => '0xD79F',
2667
				'0xF0' => '0xD7A0',		'0xF1' => '0xD7A1',		'0xF2' => '0xD7A2',
2668
				'0xF3' => '0xD7A3',		'0xF5' => '0xD7A5',		'0xF6' => '0xD7A6',
2669
				'0xF7' => '0xD7A7',		'0xF8' => '0xD7A8',		'0xF9' => '0xD7A9',
2670
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2671
				'0x86' => '0xE280A0',	'0x87' => '0xE280A1',	'0x89' => '0xE280B0',
2672
				'0x8B' => '0xE280B9',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2673
				'0x95' => '0xE280A2',	'0x97' => '0xE28094',	'0x99' => '0xE284A2',
2674
				'0xC0' => '0xD6B0',		'0xC1' => '0xD6B1',		'0xC3' => '0xD6B3',
2675
				'0xC4' => '0xD6B4',		'0xC5' => '0xD6B5',		'0xC6' => '0xD6B6',
2676
				'0xC7' => '0xD6B7',		'0xC8' => '0xD6B8',		'0xC9' => '0xD6B9',
2677
				'0xCB' => '0xD6BB',		'0xCC' => '0xD6BC',		'0xCD' => '0xD6BD',
2678
				'0xCE' => '0xD6BE',		'0xCF' => '0xD6BF',		'0xD0' => '0xD780',
2679
				'0xD2' => '0xD782',		'0xE3' => '0xD793',		'0xE4' => '0xD794',
2680
				'0xE5' => '0xD795',		'0xE7' => '0xD797',		'0xE9' => '0xD799',
2681
				'0xFD' => '0xE2808E',	'0xFE' => '0xE2808F',	'0x92' => '0xE28099',
2682
				'0x83' => '0xC692',		'0xD3' => '0xD783',		'0x88' => '0xCB86',
2683
				'0x98' => '0xCB9C',		'0x91' => '0xE28098',	'0x96' => '0xE28093',
2684
				'0xBA' => '0xC3B7',		'0x9B' => '0xE280BA',	'0xAA' => '0xC397',
2685
				'0xA4' => '0xE282AA',	'0xE1' => '0xD791',		'0xE6' => '0xD796',
2686
				'0xE8' => '0xD798',		'0xEB' => '0xD79B',		'0xF4' => '0xD7A4',
2687
				'0xFA' => '0xD7AA',
2688
			),
2689
			'windows-1253' => array(
2690
				'0x81' => '\'\'',			'0x88' => '\'\'',			'0x8A' => '\'\'',
2691
				'0x8C' => '\'\'',			'0x8D' => '\'\'',			'0x8E' => '\'\'',
2692
				'0x8F' => '\'\'',			'0x90' => '\'\'',			'0x98' => '\'\'',
2693
				'0x9A' => '\'\'',			'0x9C' => '\'\'',			'0x9D' => '\'\'',
2694
				'0x9E' => '\'\'',			'0x9F' => '\'\'',			'0xAA' => '\'\'',
2695
				'0xD2' => '0xE282AC',			'0xFF' => '0xCE92',			'0xCE' => '0xCE9E',
2696
				'0xB8' => '0xCE88',		'0xBA' => '0xCE8A',		'0xBC' => '0xCE8C',
2697
				'0xBE' => '0xCE8E',		'0xBF' => '0xCE8F',		'0xC0' => '0xCE90',
2698
				'0xC8' => '0xCE98',		'0xCA' => '0xCE9A',		'0xCC' => '0xCE9C',
2699
				'0xCD' => '0xCE9D',		'0xCF' => '0xCE9F',		'0xDA' => '0xCEAA',
2700
				'0xE8' => '0xCEB8',		'0xEA' => '0xCEBA',		'0xEC' => '0xCEBC',
2701
				'0xEE' => '0xCEBE',		'0xEF' => '0xCEBF',		'0xC2' => '0xFF',
2702
				'0xBD' => '0xC2BD',		'0xED' => '0xCEBD',		'0xB2' => '0xC2B2',
2703
				'0xA0' => '0xC2A0',		'0xA3' => '0xC2A3',		'0xA4' => '0xC2A4',
2704
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2705
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2706
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2707
				'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',		'0xB3' => '0xC2B3',
2708
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2709
				'0xBB' => '0xC2BB',		'0xE2' => '0xCEB2',		'0x80' => '0xD2',
2710
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2711
				'0x86' => '0xE280A0',	'0xA1' => '0xCE85',		'0xA2' => '0xCE86',
2712
				'0x87' => '0xE280A1',	'0x89' => '0xE280B0',	'0xB9' => '0xCE89',
2713
				'0x8B' => '0xE280B9',	'0x91' => '0xE28098',	'0x99' => '0xE284A2',
2714
				'0x92' => '0xE28099',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2715
				'0x95' => '0xE280A2',	'0x96' => '0xE28093',	'0x97' => '0xE28094',
2716
				'0x9B' => '0xE280BA',	'0xAF' => '0xE28095',	'0xB4' => '0xCE84',
2717
				'0xC1' => '0xCE91',		'0xC3' => '0xCE93',		'0xC4' => '0xCE94',
2718
				'0xC5' => '0xCE95',		'0xC6' => '0xCE96',		'0x83' => '0xC692',
2719
				'0xC7' => '0xCE97',		'0xC9' => '0xCE99',		'0xCB' => '0xCE9B',
2720
				'0xD0' => '0xCEA0',		'0xD1' => '0xCEA1',		'0xD3' => '0xCEA3',
2721
				'0xD4' => '0xCEA4',		'0xD5' => '0xCEA5',		'0xD6' => '0xCEA6',
2722
				'0xD7' => '0xCEA7',		'0xD8' => '0xCEA8',		'0xD9' => '0xCEA9',
2723
				'0xDB' => '0xCEAB',		'0xDC' => '0xCEAC',		'0xDD' => '0xCEAD',
2724
				'0xDE' => '0xCEAE',		'0xDF' => '0xCEAF',		'0xE0' => '0xCEB0',
2725
				'0xE1' => '0xCEB1',		'0xE3' => '0xCEB3',		'0xE4' => '0xCEB4',
2726
				'0xE5' => '0xCEB5',		'0xE6' => '0xCEB6',		'0xE7' => '0xCEB7',
2727
				'0xE9' => '0xCEB9',		'0xEB' => '0xCEBB',		'0xF0' => '0xCF80',
2728
				'0xF1' => '0xCF81',		'0xF2' => '0xCF82',		'0xF3' => '0xCF83',
2729
				'0xF4' => '0xCF84',		'0xF5' => '0xCF85',		'0xF6' => '0xCF86',
2730
				'0xF7' => '0xCF87',		'0xF8' => '0xCF88',		'0xF9' => '0xCF89',
2731
				'0xFA' => '0xCF8A',		'0xFB' => '0xCF8B',		'0xFC' => '0xCF8C',
2732
				'0xFD' => '0xCF8D',		'0xFE' => '0xCF8E',
2733
			),
2734
		);
2735
2736
		// Make some preparations.
2737
		if (isset($translation_tables[$upcontext['charset_detected']]))
2738
		{
2739
			$replace = '%field%';
2740
2741
			// Build a huge REPLACE statement...
2742
			foreach ($translation_tables[$upcontext['charset_detected']] as $from => $to)
2743
				$replace = 'REPLACE(' . $replace . ', ' . $from . ', ' . $to . ')';
2744
		}
2745
2746
		// Get a list of table names ahead of time... This makes it easier to set our substep and such
2747
		db_extend();
2748
		$queryTables = $smcFunc['db_list_tables'](false, $db_prefix . '%');
2749
2750
		$upcontext['table_count'] = count($queryTables);
2751
2752
		// What ones have we already done?
2753
		foreach ($queryTables as $id => $table)
2754
			if ($id < $_GET['substep'])
2755
				$upcontext['previous_tables'][] = $table;
2756
2757
		$upcontext['cur_table_num'] = $_GET['substep'];
2758
		$upcontext['cur_table_name'] = str_replace($db_prefix, '', $queryTables[$_GET['substep']]);
2759
		$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2760
2761
		// Make sure we're ready & have painted the template before proceeding
2762
		if ($support_js && !isset($_GET['xml'])) {
2763
			$_GET['substep'] = 0;
2764
			return false;
2765
		}
2766
2767
		// We want to start at the first table.
2768
		for ($substep = $_GET['substep'], $n = count($queryTables); $substep < $n; $substep++)
2769
		{
2770
			$table = $queryTables[$substep];
2771
2772
			$getTableStatus = $smcFunc['db_query']('', '
2773
				SHOW TABLE STATUS
2774
				LIKE {string:table_name}',
2775
				array(
2776
					'table_name' => str_replace('_', '\_', $table)
2777
				)
2778
			);
2779
2780
			// Only one row so we can just fetch_assoc and free the result...
2781
			$table_info = $smcFunc['db_fetch_assoc']($getTableStatus);
2782
			$smcFunc['db_free_result']($getTableStatus);
2783
2784
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($queryTables[$substep + 1]) ? $queryTables[$substep + 1] : $queryTables[$substep]));
2785
			$upcontext['cur_table_num'] = $substep + 1;
2786
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2787
2788
			// Do we need to pause?
2789
			nextSubstep($substep);
2790
2791
			// Just to make sure it doesn't time out.
2792
			if (function_exists('apache_reset_timeout'))
2793
				@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...
2794
2795
			$table_charsets = array();
2796
2797
			// Loop through each column.
2798
			$queryColumns = $smcFunc['db_query']('', '
2799
				SHOW FULL COLUMNS
2800
				FROM ' . $table_info['Name'],
2801
				array(
2802
				)
2803
			);
2804
			while ($column_info = $smcFunc['db_fetch_assoc']($queryColumns))
2805
			{
2806
				// Only text'ish columns have a character set and need converting.
2807
				if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false)
2808
				{
2809
					$collation = empty($column_info['Collation']) || $column_info['Collation'] === 'NULL' ? $table_info['Collation'] : $column_info['Collation'];
2810
					if (!empty($collation) && $collation !== 'NULL')
2811
					{
2812
						list($charset) = explode('_', $collation);
2813
2814
						// Build structure of columns to operate on organized by charset; only operate on columns not yet utf8
2815
						if ($charset != 'utf8') {
2816
							if (!isset($table_charsets[$charset]))
2817
								$table_charsets[$charset] = array();
2818
2819
							$table_charsets[$charset][] = $column_info;
2820
						}
2821
					}
2822
				}
2823
			}
2824
			$smcFunc['db_free_result']($queryColumns);
2825
2826
			// Only change the non-utf8 columns identified above
2827
			if (count($table_charsets) > 0)
2828
			{
2829
				$updates_blob = '';
2830
				$updates_text = '';
2831
				foreach ($table_charsets as $charset => $columns)
2832
				{
2833
					if ($charset !== $charsets[$upcontext['charset_detected']])
2834
					{
2835
						foreach ($columns as $column)
2836
						{
2837
							$updates_blob .= '
2838
								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'] . '\'') . ',';
2839
							$updates_text .= '
2840
								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'] . '\'') . ',';
2841
						}
2842
					}
2843
				}
2844
2845
				// Change the columns to binary form.
2846
				$smcFunc['db_query']('', '
2847
					ALTER TABLE {raw:table_name}{raw:updates_blob}',
2848
					array(
2849
						'table_name' => $table_info['Name'],
2850
						'updates_blob' => substr($updates_blob, 0, -1),
2851
					)
2852
				);
2853
2854
				// Convert the character set if MySQL has no native support for it.
2855
				if (isset($translation_tables[$upcontext['charset_detected']]))
2856
				{
2857
					$update = '';
2858
					foreach ($table_charsets as $charset => $columns)
2859
						foreach ($columns as $column)
2860
							$update .= '
2861
								' . $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...
2862
2863
					$smcFunc['db_query']('', '
2864
						UPDATE {raw:table_name}
2865
						SET {raw:updates}',
2866
						array(
2867
							'table_name' => $table_info['Name'],
2868
							'updates' => substr($update, 0, -1),
2869
						)
2870
					);
2871
				}
2872
2873
				// Change the columns back, but with the proper character set.
2874
				$smcFunc['db_query']('', '
2875
					ALTER TABLE {raw:table_name}{raw:updates_text}',
2876
					array(
2877
						'table_name' => $table_info['Name'],
2878
						'updates_text' => substr($updates_text, 0, -1),
2879
					)
2880
				);
2881
			}
2882
2883
			// Now do the actual conversion (if still needed).
2884
			if ($charsets[$upcontext['charset_detected']] !== 'utf8')
2885
			{
2886
				if ($command_line)
2887
					echo 'Converting table ' . $table_info['Name'] . ' to UTF-8...';
2888
2889
				$smcFunc['db_query']('', '
2890
					ALTER TABLE {raw:table_name}
2891
					CONVERT TO CHARACTER SET utf8',
2892
					array(
2893
						'table_name' => $table_info['Name'],
2894
					)
2895
				);
2896
2897
				if ($command_line)
2898
					echo " done.\n";
2899
			}
2900
			// If this is XML to keep it nice for the user do one table at a time anyway!
2901
			if (isset($_GET['xml']) && $upcontext['cur_table_num'] < $upcontext['table_count'])
2902
				return upgradeExit();
2903
		}
2904
2905
		$prev_charset = empty($translation_tables[$upcontext['charset_detected']]) ? $charsets[$upcontext['charset_detected']] : $translation_tables[$upcontext['charset_detected']];
2906
2907
		$smcFunc['db_insert']('replace',
2908
			'{db_prefix}settings',
2909
			array('variable' => 'string', 'value' => 'string'),
2910
			array(array('global_character_set', 'UTF-8'), array('previousCharacterSet', $prev_charset)),
2911
			array('variable')
2912
		);
2913
2914
		// Store it in Settings.php too because it's needed before db connection.
2915
		// Hopefully this works...
2916
		require_once($sourcedir . '/Subs-Admin.php');
2917
		updateSettingsFile(array('db_character_set' => '\'utf8\''));
2918
2919
		// The conversion might have messed up some serialized strings. Fix them!
2920
		$request = $smcFunc['db_query']('', '
2921
			SELECT id_action, extra
2922
			FROM {db_prefix}log_actions
2923
			WHERE action IN ({string:remove}, {string:delete})',
2924
			array(
2925
				'remove' => 'remove',
2926
				'delete' => 'delete',
2927
			)
2928
		);
2929
		while ($row = $smcFunc['db_fetch_assoc']($request))
2930
		{
2931
			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)
2932
				$smcFunc['db_query']('', '
2933
					UPDATE {db_prefix}log_actions
2934
					SET extra = {string:extra}
2935
					WHERE id_action = {int:current_action}',
2936
					array(
2937
						'current_action' => $row['id_action'],
2938
						'extra' => $matches[1] . strlen($matches[3]) . ':"' . $matches[3] . '"' . $matches[4],
2939
					)
2940
				);
2941
		}
2942
		$smcFunc['db_free_result']($request);
2943
2944
		if ($upcontext['dropping_index'] && $command_line)
2945
		{
2946
			echo "\nYour fulltext search index was dropped to facilitate the conversion. You will need to recreate it.";
2947
			flush();
2948
		}
2949
	}
2950
	$_GET['substep'] = 0;
2951
	return false;
2952
}
2953
2954
function serialize_to_json()
2955
{
2956
	global $command_line, $smcFunc, $modSettings, $sourcedir, $upcontext, $support_js;
2957
2958
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'serialize_json_xml' : 'serialize_json';
2959
	// First thing's first - did we already do this?
2960
	if (!empty($modSettings['json_done']))
2961
	{
2962
		if ($command_line)
2963
			return DeleteUpgrade();
2964
		else
2965
			return true;
2966
	}
2967
2968
	// Done it already - js wise?
2969
	if (!empty($_POST['json_done']))
2970
		return true;
2971
2972
	// List of tables affected by this function
2973
	// 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...
2974
	// If 3rd item in array is true, it indicates that col1 could be empty...
2975
	$tables = array(
2976
		'background_tasks' => array('id_task', 'task_data'),
2977
		'log_actions' => array('id_action', 'extra'),
2978
		'log_online' => array('session', 'url'),
2979
		'log_packages' => array('id_install', 'db_changes', 'failed_steps', 'credits'),
2980
		'log_spider_hits' => array('id_hit', 'url'),
2981
		'log_subscribed' => array('id_sublog', 'pending_details'),
2982
		'pm_rules' => array('id_rule', 'criteria', 'actions'),
2983
		'qanda' => array('id_question', 'answers'),
2984
		'subscriptions' => array('id_subscribe', 'cost'),
2985
		'user_alerts' => array('id_alert', 'extra', true),
2986
		'user_drafts' => array('id_draft', 'to_list', true),
2987
		// These last two are a bit different - we'll handle those separately
2988
		'settings' => array(),
2989
		'themes' => array()
2990
	);
2991
2992
	// Set up some context stuff...
2993
	// Because we're not using numeric indices, we need this to figure out the current table name...
2994
	$keys = array_keys($tables);
2995
2996
	$upcontext['page_title'] = 'Converting to JSON';
2997
	$upcontext['table_count'] = count($keys);
2998
	$upcontext['cur_table_num'] = $_GET['substep'];
2999
	$upcontext['cur_table_name'] = isset($keys[$_GET['substep']]) ? $keys[$_GET['substep']] : $keys[0];
3000
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3001
3002 View Code Duplication
	foreach ($keys as $id => $table)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

Loading history...
3399
		echo '
3400
						<li class="', $num < $upcontext['current_step'] ? 'stepdone' : ($num == $upcontext['current_step'] ? 'stepcurrent' : 'stepwaiting'), '">', $txt['upgrade_step'], ' ', $step[0], ': ', $step[1], '</li>';
3401
3402
	echo '
3403
					</ul>
3404
			</div>
3405
3406
			<div id="progress_bar">
3407
				<div id="overall_text">', $upcontext['overall_percent'], '%</div>
3408
				<div id="overall_progress" style="width: ', $upcontext['overall_percent'], '%;">
3409
					<span>', $txt['upgrade_overall_progress'], '</span>
3410
				</div>
3411
			</div>';
3412
3413
	if (isset($upcontext['step_progress']))
3414
		echo '
3415
				<br>
3416
				<br>
3417
				<div id="progress_bar_step">
3418
					<div id="step_text">', $upcontext['step_progress'], '%</div>
3419
					<div id="step_progress" style="width: ', $upcontext['step_progress'], '%;background-color: #ffd000;">
3420
						<span>', $txt['upgrade_step_progress'], '</span>
3421
					</div>
3422
				</div>';
3423
3424
	echo '
3425
				<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>
3426
				<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', ';">
3427
					<div id="substep_text" style="color: #000; position: absolute; margin-left: -5em;">', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : '', '%</div>
3428
					<div id="substep_progress" style="width: ', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%; height: 12pt; z-index: 1; background-color: #eebaf4;">&nbsp;</div>
3429
				</div>';
3430
3431
	// How long have we been running this?
3432
	$elapsed = time() - $upcontext['started'];
3433
	$mins = (int) ($elapsed / 60);
3434
	$seconds = $elapsed - $mins * 60;
3435
	echo '
3436
								<br> <br> <br> <br> <br>
3437
								<div class="smalltext" style="padding: 5px; text-align: center;"><br>', $txt['upgrade_time_elapsed'], ':
3438
									<span id="mins_elapsed">', $mins, '</span> ', $txt['upgrade_time_mins'], ', <span id="secs_elapsed">', $seconds, '</span> ', $txt['upgrade_time_secs'], '.
3439
								</div>';
3440
	echo '
3441
			</div>
3442
			</div>
3443
			<div id="main_screen" class="clear">
3444
				<h2>', $upcontext['page_title'], '</h2>
3445
				<div class="panel">
3446
					<div style="max-height: 360px; overflow: auto;">';
3447
}
3448
3449
function template_upgrade_below()
3450
{
3451
	global $upcontext, $txt;
3452
3453
	if (!empty($upcontext['pause']))
3454
		echo '
3455
								<em>', $txt['upgrade_incomplete'], '.</em><br>
3456
3457
								<h2 style="margin-top: 2ex;">', $txt['upgrade_not_quite_done'], '</h2>
3458
								<h3>
3459
									', $txt['upgrade_paused_overload'], '
3460
								</h3>';
3461
3462
	if (!empty($upcontext['custom_warning']))
3463
		echo '
3464
								<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3465
									<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3466
									<strong style="text-decoration: underline;">', $txt['upgrade_note'], '</strong><br>
3467
									<div style="padding-left: 6ex;">', $upcontext['custom_warning'], '</div>
3468
								</div>';
3469
3470
	echo '
3471
								<div class="righttext" style="margin: 1ex;">';
3472
3473
	if (!empty($upcontext['continue']))
3474
		echo '
3475
									<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '"', $upcontext['continue'] == 2 ? ' disabled' : '', ' class="button_submit">';
3476
	if (!empty($upcontext['skip']))
3477
		echo '
3478
									<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="dontSubmit = true; document.getElementById(\'contbutt\').disabled = \'disabled\'; return true;" class="button_submit">';
3479
3480
	echo '
3481
								</div>
3482
							</form>
3483
						</div>
3484
				</div>
3485
			</div>
3486
			</div>
3487
		</div>
3488
		<div id="footer">
3489
			<ul>
3490
				<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>
3491
			</ul>
3492
		</div>
3493
	</body>
3494
</html>';
3495
3496
	// Are we on a pause?
3497
	if (!empty($upcontext['pause']))
3498
	{
3499
		echo '
3500
		<script>
3501
			window.onload = doAutoSubmit;
3502
			var countdown = 3;
3503
			var dontSubmit = false;
3504
3505
			function doAutoSubmit()
3506
			{
3507
				if (countdown == 0 && !dontSubmit)
3508
					document.upform.submit();
3509
				else if (countdown == -1)
3510
					return;
3511
3512
				document.getElementById(\'contbutt\').value = "', $txt['upgrade_continue'], ' (" + countdown + ")";
3513
				countdown--;
3514
3515
				setTimeout("doAutoSubmit();", 1000);
3516
			}
3517
		</script>';
3518
	}
3519
}
3520
3521
function template_xml_above()
3522
{
3523
	global $upcontext;
3524
3525
	echo '<', '?xml version="1.0" encoding="UTF-8"?', '>
3526
	<smf>';
3527
3528
	if (!empty($upcontext['get_data']))
3529
		foreach ($upcontext['get_data'] as $k => $v)
3530
			echo '
3531
		<get key="', $k, '">', $v, '</get>';
3532
}
3533
3534
function template_xml_below()
3535
{
3536
	echo '
3537
		</smf>';
3538
}
3539
3540
function template_error_message()
3541
{
3542
	global $upcontext;
3543
3544
	echo '
3545
	<div class="error_message red">
3546
		', $upcontext['error_msg'], '
3547
		<br>
3548
		<a href="', $_SERVER['PHP_SELF'], '">Click here to try again.</a>
3549
	</div>';
3550
}
3551
3552
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...
3553
{
3554
	global $upcontext, $disable_security, $settings, $txt;
3555
3556
	echo '
3557
		<script src="https://www.simplemachines.org/smf/current-version.js?version=' . SMF_VERSION . '"></script>
3558
			<h3>', sprintf($txt['upgrade_ready_proceed'], SMF_VERSION), '</h3>
3559
	<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">
3560
		<input type="hidden" name="', $upcontext['login_token_var'], '" value="', $upcontext['login_token'], '">
3561
		<div id="version_warning" style="margin: 2ex; padding: 2ex; border: 2px dashed #a92174; color: black; background-color: #fbbbe2; display: none;">
3562
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3563
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3564
			<div style="padding-left: 6ex;">
3565
				', sprintf($txt['upgrade_warning_out_of_date'], SMF_VERSION), '
3566
			</div>
3567
		</div>';
3568
3569
	$upcontext['chmod_in_form'] = true;
3570
	template_chmod();
3571
3572
	// For large, pre 1.1 RC2 forums give them a warning about the possible impact of this upgrade!
3573
	if ($upcontext['is_large_forum'])
3574
		echo '
3575
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3576
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3577
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3578
			<div style="padding-left: 6ex;">
3579
				', $txt['upgrade_warning_lots_data'], '
3580
			</div>
3581
		</div>';
3582
3583
	// A warning message?
3584
	if (!empty($upcontext['warning']))
3585
		echo '
3586
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3587
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3588
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3589
			<div style="padding-left: 6ex;">
3590
				', $upcontext['warning'], '
3591
			</div>
3592
		</div>';
3593
3594
	// Paths are incorrect?
3595
	echo '
3596
		<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">
3597
			<div style="float: left; width: 2ex; font-size: 2em; color: black;">!!</div>
3598
			<strong style="text-decoration: underline;">', $txt['upgrade_critical_error'], '</strong><br>
3599
			<div style="padding-left: 6ex;">
3600
				', $txt['upgrade_error_script_js'], '
3601
			</div>
3602
		</div>';
3603
3604
	// Is there someone already doing this?
3605
	if (!empty($upcontext['user']['id']) && (time() - $upcontext['started'] < 72600 || time() - $upcontext['updated'] < 3600))
3606
	{
3607
		$ago = time() - $upcontext['started'];
3608
		if ($ago < 60)
3609
			$ago = $ago . ' seconds';
3610
		elseif ($ago < 3600)
3611
			$ago = (int) ($ago / 60) . ' minutes';
3612
		else
3613
			$ago = (int) ($ago / 3600) . ' hours';
3614
3615
		$active = time() - $upcontext['updated'];
3616
		if ($active < 60)
3617
			$updated = $active . ' seconds';
3618
		elseif ($active < 3600)
3619
			$updated = (int) ($active / 60) . ' minutes';
3620
		else
3621
			$updated = (int) ($active / 3600) . ' hours';
3622
3623
		echo '
3624
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3625
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3626
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3627
			<div style="padding-left: 6ex;">
3628
				&quot;', $upcontext['user']['name'], '&quot; has been running the upgrade script for the last ', $ago, ' - and was last active ', $updated, ' ago.';
3629
3630
		if ($active < 600)
3631
			echo '
3632
				We recommend that you do not run this script unless you are sure that ', $upcontext['user']['name'], ' has completed their upgrade.';
3633
3634
		if ($active > $upcontext['inactive_timeout'])
3635
			echo '
3636
				<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.';
3637
		else
3638
			echo '
3639
				<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!');
3640
3641
		echo '
3642
			</div>
3643
		</div>';
3644
	}
3645
3646
	echo '
3647
			<strong>Admin Login: ', $disable_security ? '(DISABLED)' : '', '</strong>
3648
			<h3>For security purposes please login with your admin account to proceed with the upgrade.</h3>
3649
			<table>
3650
				<tr valign="top">
3651
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Username:</strong></td>
3652
					<td>
3653
						<input type="text" name="user" value="', !empty($upcontext['username']) ? $upcontext['username'] : '', '"', $disable_security ? ' disabled' : '', '>';
3654
3655
	if (!empty($upcontext['username_incorrect']))
3656
		echo '
3657
						<div class="smalltext" style="color: red;">Username Incorrect</div>';
3658
3659
	echo '
3660
					</td>
3661
				</tr>
3662
				<tr valign="top">
3663
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Password:</strong></td>
3664
					<td>
3665
						<input type="password" name="passwrd" value=""', $disable_security ? ' disabled' : '', '>
3666
						<input type="hidden" name="hash_passwrd" value="">';
3667
3668
	if (!empty($upcontext['password_failed']))
3669
		echo '
3670
						<div class="smalltext" style="color: red;">Password Incorrect</div>';
3671
3672
	echo '
3673
					</td>
3674
				</tr>';
3675
3676
	// Can they continue?
3677
	if (!empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] >= $upcontext['inactive_timeout'] && $upcontext['user']['step'] > 1)
3678
	{
3679
		echo '
3680
				<tr>
3681
					<td colspan="2">
3682
						<label for="cont"><input type="checkbox" id="cont" name="cont" checked>Continue from step reached during last execution of upgrade script.</label>
3683
					</td>
3684
				</tr>';
3685
	}
3686
3687
	echo '
3688
			</table><br>
3689
			<span class="smalltext">
3690
				<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.
3691
			</span>
3692
			<input type="hidden" name="login_attempt" id="login_attempt" value="1">
3693
			<input type="hidden" name="js_works" id="js_works" value="0">';
3694
3695
	// Say we want the continue button!
3696
	$upcontext['continue'] = !empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] < $upcontext['inactive_timeout'] ? 2 : 1;
3697
3698
	// This defines whether javascript is going to work elsewhere :D
3699
	echo '
3700
		<script>
3701
			if (\'XMLHttpRequest\' in window && document.getElementById(\'js_works\'))
3702
				document.getElementById(\'js_works\').value = 1;
3703
3704
			// Latest version?
3705
			function smfCurrentVersion()
3706
			{
3707
				var smfVer, yourVer;
3708
3709
				if (!(\'smfVersion\' in window))
3710
					return;
3711
3712
				window.smfVersion = window.smfVersion.replace(/SMF\s?/g, \'\');
3713
3714
				smfVer = document.getElementById(\'smfVersion\');
3715
				yourVer = document.getElementById(\'yourVersion\');
3716
3717
				setInnerHTML(smfVer, window.smfVersion);
3718
3719
				var currentVersion = getInnerHTML(yourVer);
3720
				if (currentVersion < window.smfVersion)
3721
					document.getElementById(\'version_warning\').style.display = \'\';
3722
			}
3723
			addLoadEvent(smfCurrentVersion);
3724
3725
			// This checks that the script file even exists!
3726
			if (typeof(smfSelectText) == \'undefined\')
3727
				document.getElementById(\'js_script_missing_error\').style.display = \'\';
3728
3729
		</script>';
3730
}
3731
3732
function template_upgrade_options()
3733
{
3734
	global $upcontext, $modSettings, $db_prefix, $mmessage, $mtitle;
3735
3736
	echo '
3737
			<h3>Before the upgrade gets underway please review the options below - and hit continue when you\'re ready to begin.</h3>
3738
			<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">';
3739
3740
	// Warning message?
3741
	if (!empty($upcontext['upgrade_options_warning']))
3742
		echo '
3743
		<div style="margin: 1ex; padding: 1ex; border: 1px dashed #cc3344; color: black; background-color: #ffe4e9;">
3744
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3745
			<strong style="text-decoration: underline;">Warning!</strong><br>
3746
			<div style="padding-left: 4ex;">
3747
				', $upcontext['upgrade_options_warning'], '
3748
			</div>
3749
		</div>';
3750
3751
	echo '
3752
				<table>
3753
					<tr valign="top">
3754
						<td width="2%">
3755
							<input type="checkbox" name="backup" id="backup" value="1">
3756
						</td>
3757
						<td width="100%">
3758
							<label for="backup">Backup tables in your database with the prefix &quot;backup_' . $db_prefix . '&quot;.</label> (recommended!)
3759
						</td>
3760
					</tr>
3761
					<tr valign="top">
3762
						<td width="2%">
3763
							<input type="checkbox" name="maint" id="maint" value="1" checked>
3764
						</td>
3765
						<td width="100%">
3766
							<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>
3767
							<div id="mainmess" style="display: none;">
3768
								<strong class="smalltext">Maintenance Title: </strong><br>
3769
								<input type="text" name="maintitle" size="30" value="', htmlspecialchars($mtitle), '"><br>
3770
								<strong class="smalltext">Maintenance Message: </strong><br>
3771
								<textarea name="mainmessage" rows="3" cols="50">', htmlspecialchars($mmessage), '</textarea>
3772
							</div>
3773
						</td>
3774
					</tr>
3775
					<tr valign="top">
3776
						<td width="2%">
3777
							<input type="checkbox" name="debug" id="debug" value="1">
3778
						</td>
3779
						<td width="100%">
3780
							<label for="debug">Output extra debugging information</label>
3781
						</td>
3782
					</tr>
3783
					<tr valign="top">
3784
						<td width="2%">
3785
							<input type="checkbox" name="empty_error" id="empty_error" value="1">
3786
						</td>
3787
						<td width="100%">
3788
							<label for="empty_error">Empty error log before upgrading</label>
3789
						</td>
3790
					</tr>';
3791
3792
	if (!empty($upcontext['karma_installed']['good']) || !empty($upcontext['karma_installed']['bad']))
3793
		echo '
3794
					<tr valign="top">
3795
						<td width="2%">
3796
							<input type="checkbox" name="delete_karma" id="delete_karma" value="1">
3797
						</td>
3798
						<td width="100%">
3799
							<label for="delete_karma">Delete all karma settings and info from the DB</label>
3800
						</td>
3801
					</tr>';
3802
3803
	echo '
3804
					<tr valign="top">
3805
						<td width="2%">
3806
							<input type="checkbox" name="stats" id="stats" value="1"', empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']) ? '' : ' checked="checked"', ' />
3807
						</td>
3808
						<td width="100%">
3809
							<label for="stat">
3810
								Allow Simple Machines to Collect Basic Stats Monthly.<br>
3811
								<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>
3812
							</label>
3813
						</td>
3814
					</tr>
3815
				</table>
3816
				<input type="hidden" name="upcont" value="1">';
3817
3818
	// We need a normal continue button here!
3819
	$upcontext['continue'] = 1;
3820
}
3821
3822
// Template for the database backup tool/
3823
function template_backup_database()
3824
{
3825
	global $upcontext, $support_js, $is_debug;
3826
3827
	echo '
3828
			<h3>Please wait while a backup is created. For large forums this may take some time!</h3>';
3829
3830
	echo '
3831
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
3832
			<input type="hidden" name="backup_done" id="backup_done" value="0">
3833
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
3834
			<div id="debug_section" style="height: ', ($is_debug ? '115' : '12') , 'px; overflow: auto;">
3835
			<span id="debuginfo"></span>
3836
			</div>';
3837
3838
	// Dont any tables so far?
3839
	if (!empty($upcontext['previous_tables']))
3840
		foreach ($upcontext['previous_tables'] as $table)
3841
			echo '
3842
			<br>Completed Table: &quot;', $table, '&quot;.';
3843
3844
	echo '
3845
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
3846
			<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>';
3847
3848
	// Continue please!
3849
	$upcontext['continue'] = $support_js ? 2 : 1;
3850
3851
	// If javascript allows we want to do this using XML.
3852
	if ($support_js)
3853
	{
3854
		echo '
3855
		<script>
3856
			var lastTable = ', $upcontext['cur_table_num'], ';
3857
			function getNextTables()
3858
			{
3859
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
3860
			}
3861
3862
			// Got an update!
3863
			function onBackupUpdate(oXMLDoc)
3864
			{
3865
				var sCurrentTableName = "";
3866
				var iTableNum = 0;
3867
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
3868
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
3869
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
3870
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
3871
3872
				// Update the page.
3873
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
3874
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
3875
				lastTable = iTableNum;
3876
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
3877
3878
		// If debug flood the screen.
3879
		if ($is_debug)
3880
			echo '
3881
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
3882
3883
				if (document.getElementById(\'debug_section\').scrollHeight)
3884
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
3885
3886
		echo '
3887
				// Get the next update...
3888
				if (iTableNum == ', $upcontext['table_count'], ')
3889
				{
3890
					document.getElementById(\'commess\').style.display = "";
3891
					document.getElementById(\'current_tab_div\').style.display = "none";
3892
					document.getElementById(\'contbutt\').disabled = 0;
3893
					document.getElementById(\'backup_done\').value = 1;
3894
				}
3895
				else
3896
					getNextTables();
3897
			}
3898
			getNextTables();
3899
		//# sourceURL=dynamicScript-bkup.js
3900
		</script>';
3901
	}
3902
}
3903
3904
function template_backup_xml()
3905
{
3906
	global $upcontext;
3907
3908
	echo '
3909
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
3910
}
3911
3912
// Here is the actual "make the changes" template!
3913
function template_database_changes()
3914
{
3915
	global $upcontext, $support_js, $is_debug, $timeLimitThreshold;
3916
3917
	if (empty($is_debug) && !empty($upcontext['upgrade_status']['debug']))
3918
		$is_debug = true;
3919
3920
	echo '
3921
		<h3>Executing database changes</h3>
3922
		<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>';
3923
3924
	echo '
3925
		<form action="', $upcontext['form_url'], '&amp;filecount=', $upcontext['file_count'], '" name="upform" id="upform" method="post">
3926
		<input type="hidden" name="database_done" id="database_done" value="0">';
3927
3928
	// No javascript looks rubbish!
3929
	if (!$support_js)
3930
	{
3931
		foreach ($upcontext['actioned_items'] as $num => $item)
3932
		{
3933
			if ($num != 0)
3934
				echo ' Successful!';
3935
			echo '<br>' . $item;
3936
		}
3937 View Code Duplication
		if (!empty($upcontext['changes_complete']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3938
		{
3939
			if ($is_debug)
3940
			{
3941
				$active = time() - $upcontext['started'];
3942
				$hours = floor($active / 3600);
3943
				$minutes = intval(($active / 60) % 60);
3944
				$seconds = intval($active % 60);
3945
3946
				$totalTime = '';
3947
				if ($hours > 0)
3948
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
3949
				if ($minutes > 0)
3950
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
3951
				if ($seconds > 0)
3952
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
3953
			}
3954
3955
			if ($is_debug && !empty($totalTime))
3956
				echo ' Successful! Completed in ', $totalTime, '<br><br>';
3957
			else
3958
				echo ' Successful!<br><br>';
3959
3960
			echo '<span id="commess" style="font-weight: bold;">1 Database Updates Complete! Click Continue to Proceed.</span><br>';
3961
		}
3962
	}
3963
	else
3964
	{
3965
		// Tell them how many files we have in total.
3966
		if ($upcontext['file_count'] > 1)
3967
			echo '
3968
		<strong id="info1">Executing upgrade script <span id="file_done">', $upcontext['cur_file_num'], '</span> of ', $upcontext['file_count'], '.</strong>';
3969
3970
		echo '
3971
		<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>
3972
		<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>';
3973
3974 View Code Duplication
		if ($is_debug)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3975
		{
3976
			if ($upcontext['current_debug_item_num'] == $upcontext['debug_items'])
3977
			{
3978
				$active = time() - $upcontext['started'];
3979
				$hours = floor($active / 3600);
3980
				$minutes = intval(($active / 60) % 60);
3981
				$seconds = intval($active % 60);
3982
3983
				$totalTime = '';
3984
				if ($hours > 0)
3985
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
3986
				if ($minutes > 0)
3987
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
3988
				if ($seconds > 0)
3989
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
3990
			}
3991
3992
			echo '
3993
			<br><span id="upgradeCompleted">';
3994
3995
			if (!empty($totalTime))
3996
				echo 'Completed in ', $totalTime, '<br>';
3997
3998
			echo '</span>
3999
			<div id="debug_section" style="height: 59px; overflow: auto;">
4000
			<span id="debuginfo"></span>
4001
			</div>';
4002
		}
4003
	}
4004
4005
	// Place for the XML error message.
4006
	echo '
4007
		<div id="error_block" style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9; display: ', empty($upcontext['error_message']) ? 'none' : '', ';">
4008
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4009
			<strong style="text-decoration: underline;">Error!</strong><br>
4010
			<div style="padding-left: 6ex;" id="error_message">', isset($upcontext['error_message']) ? $upcontext['error_message'] : 'Unknown Error!', '</div>
4011
		</div>';
4012
4013
	// We want to continue at some point!
4014
	$upcontext['continue'] = $support_js ? 2 : 1;
4015
4016
	// If javascript allows we want to do this using XML.
4017
	if ($support_js)
4018
	{
4019
		echo '
4020
		<script>
4021
			var lastItem = ', $upcontext['current_debug_item_num'], ';
4022
			var sLastString = "', strtr($upcontext['current_debug_item_name'], array('"' => '&quot;')), '";
4023
			var iLastSubStepProgress = -1;
4024
			var curFile = ', $upcontext['cur_file_num'], ';
4025
			var totalItems = 0;
4026
			var prevFile = 0;
4027
			var retryCount = 0;
4028
			var testvar = 0;
4029
			var timeOutID = 0;
4030
			var getData = "";
4031
			var debugItems = ', $upcontext['debug_items'], ';';
4032
4033
		if ($is_debug)
4034
			echo '
4035
			var upgradeStartTime = ' . $upcontext['started'] . ';';
4036
4037
		echo '
4038
			function getNextItem()
4039
			{
4040
				// We want to track this...
4041
				if (timeOutID)
4042
					clearTimeout(timeOutID);
4043
				timeOutID = window.setTimeout("retTimeout()", ', $timeLimitThreshold, '000);
4044
4045
				getXMLDocument(\'', $upcontext['form_url'], '&xml&filecount=', $upcontext['file_count'], '&substep=\' + lastItem + getData, onItemUpdate);
4046
			}
4047
4048
			// Got an update!
4049
			function onItemUpdate(oXMLDoc)
4050
			{
4051
				var sItemName = "";
4052
				var sDebugName = "";
4053
				var iItemNum = 0;
4054
				var iSubStepProgress = -1;
4055
				var iDebugNum = 0;
4056
				var bIsComplete = 0;
4057
				getData = "";
4058
4059
				// We\'ve got something - so reset the timeout!
4060
				if (timeOutID)
4061
					clearTimeout(timeOutID);
4062
4063
				// Assume no error at this time...
4064
				document.getElementById("error_block").style.display = "none";
4065
4066
				// Are we getting some duff info?
4067
				if (!oXMLDoc.getElementsByTagName("item")[0])
4068
				{
4069
					// Too many errors?
4070
					if (retryCount > 15)
4071
					{
4072
						document.getElementById("error_block").style.display = "";
4073
						setInnerHTML(document.getElementById("error_message"), "Error retrieving information on step: " + (sDebugName == "" ? sLastString : sDebugName));';
4074
4075
	if ($is_debug)
4076
		echo '
4077
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4078
4079
	echo '
4080
					}
4081
					else
4082
					{
4083
						retryCount++;
4084
						getNextItem();
4085
					}
4086
					return false;
4087
				}
4088
4089
				// Never allow loops.
4090
				if (curFile == prevFile)
4091
				{
4092
					retryCount++;
4093
					if (retryCount > 10)
4094
					{
4095
						document.getElementById("error_block").style.display = "";
4096
						setInnerHTML(document.getElementById("error_message"), "Upgrade script appears to be going into a loop - step: " + sDebugName);';
4097
4098
	if ($is_debug)
4099
		echo '
4100
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4101
4102
	echo '
4103
					}
4104
				}
4105
				retryCount = 0;
4106
4107
				for (var i = 0; i < oXMLDoc.getElementsByTagName("item")[0].childNodes.length; i++)
4108
					sItemName += oXMLDoc.getElementsByTagName("item")[0].childNodes[i].nodeValue;
4109
				for (var i = 0; i < oXMLDoc.getElementsByTagName("debug")[0].childNodes.length; i++)
4110
					sDebugName += oXMLDoc.getElementsByTagName("debug")[0].childNodes[i].nodeValue;
4111
				for (var i = 0; i < oXMLDoc.getElementsByTagName("get").length; i++)
4112
				{
4113
					getData += "&" + oXMLDoc.getElementsByTagName("get")[i].getAttribute("key") + "=";
4114
					for (var j = 0; j < oXMLDoc.getElementsByTagName("get")[i].childNodes.length; j++)
4115
					{
4116
						getData += oXMLDoc.getElementsByTagName("get")[i].childNodes[j].nodeValue;
4117
					}
4118
				}
4119
4120
				iItemNum = oXMLDoc.getElementsByTagName("item")[0].getAttribute("num");
4121
				iDebugNum = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("num"));
4122
				bIsComplete = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("complete"));
4123
				iSubStepProgress = parseFloat(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("percent"));
4124
				sLastString = sDebugName + " (Item: " + iDebugNum + ")";
4125
4126
				curFile = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("num"));
4127
				debugItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("debug_items"));
4128
				totalItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("items"));
4129
4130
				// If we have an error we haven\'t completed!
4131
				if (oXMLDoc.getElementsByTagName("error")[0] && bIsComplete)
4132
					iDebugNum = lastItem;
4133
4134
				// Do we have the additional progress bar?
4135
				if (iSubStepProgress != -1)
4136
				{
4137
					document.getElementById("substep_bar_div").style.display = "";
4138
					document.getElementById("substep_bar_div2").style.display = "";
4139
					document.getElementById("substep_progress").style.width = iSubStepProgress + "%";
4140
					setInnerHTML(document.getElementById("substep_text"), iSubStepProgress + "%");
4141
					setInnerHTML(document.getElementById("substep_bar_div"), sDebugName.replace(/\./g, "") + ":");
4142
				}
4143
				else
4144
				{
4145
					document.getElementById("substep_bar_div").style.display = "none";
4146
					document.getElementById("substep_bar_div2").style.display = "none";
4147
				}
4148
4149
				// Move onto the next item?
4150
				if (bIsComplete)
4151
					lastItem = iDebugNum;
4152
				else
4153
					lastItem = iDebugNum - 1;
4154
4155
				// Are we finished?
4156
				if (bIsComplete && iDebugNum == -1 && curFile >= ', $upcontext['file_count'], ')
4157
				{';
4158
4159
		if ($is_debug)
4160
			echo '
4161
					document.getElementById(\'debug_section\').style.display = "none";
4162
4163
					var upgradeFinishedTime = parseInt(oXMLDoc.getElementsByTagName("curtime")[0].childNodes[0].nodeValue);
4164
					var diffTime = upgradeFinishedTime - upgradeStartTime;
4165
					var diffHours = Math.floor(diffTime / 3600);
4166
					var diffMinutes = parseInt((diffTime / 60) % 60);
4167
					var diffSeconds = parseInt(diffTime % 60);
4168
4169
					var totalTime = "";
4170
					if (diffHours > 0)
4171
						totalTime = totalTime + diffHours + " hour" + (diffHours > 1 ? "s" : "") + " ";
4172
					if (diffMinutes > 0)
4173
						totalTime = totalTime + diffMinutes + " minute" + (diffMinutes > 1 ? "s" : "") + " ";
4174
					if (diffSeconds > 0)
4175
						totalTime = totalTime + diffSeconds + " second" + (diffSeconds > 1 ? "s" : "");
4176
4177
					setInnerHTML(document.getElementById("upgradeCompleted"), "Completed in " + totalTime);';
4178
4179
		echo '
4180
4181
					document.getElementById(\'commess\').style.display = "";
4182
					document.getElementById(\'contbutt\').disabled = 0;
4183
					document.getElementById(\'database_done\').value = 1;';
4184
4185
		if ($upcontext['file_count'] > 1)
4186
			echo '
4187
					document.getElementById(\'info1\').style.display = "none";';
4188
4189
		echo '
4190
					document.getElementById(\'info2\').style.display = "none";
4191
					updateStepProgress(100, 100, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4192
					return true;
4193
				}
4194
				// Was it the last step in the file?
4195
				else if (bIsComplete && iDebugNum == -1)
4196
				{
4197
					lastItem = 0;
4198
					prevFile = curFile;';
4199
4200
		if ($is_debug)
4201
			echo '
4202
					setOuterHTML(document.getElementById(\'debuginfo\'), \'Moving to next script file...done<br><span id="debuginfo"><\' + \'/span>\');';
4203
4204
		echo '
4205
					getNextItem();
4206
					return true;
4207
				}';
4208
4209
		// If debug scroll the screen.
4210
		if ($is_debug)
4211
			echo '
4212
				if (iLastSubStepProgress == -1)
4213
				{
4214
					// Give it consistent dots.
4215
					dots = sDebugName.match(/\./g);
4216
					numDots = dots ? dots.length : 0;
4217
					for (var i = numDots; i < 3; i++)
4218
						sDebugName += ".";
4219
					setOuterHTML(document.getElementById(\'debuginfo\'), sDebugName + \'<span id="debuginfo"><\' + \'/span>\');
4220
				}
4221
				iLastSubStepProgress = iSubStepProgress;
4222
4223
				if (bIsComplete)
4224
					setOuterHTML(document.getElementById(\'debuginfo\'), \'done<br><span id="debuginfo"><\' + \'/span>\');
4225
				else
4226
					setOuterHTML(document.getElementById(\'debuginfo\'), \'...<span id="debuginfo"><\' + \'/span>\');
4227
4228
				if (document.getElementById(\'debug_section\').scrollHeight)
4229
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4230
4231
		echo '
4232
				// Update the page.
4233
				setInnerHTML(document.getElementById(\'item_num\'), iItemNum);
4234
				setInnerHTML(document.getElementById(\'cur_item_name\'), sItemName);';
4235
4236
		if ($upcontext['file_count'] > 1)
4237
		{
4238
			echo '
4239
				setInnerHTML(document.getElementById(\'file_done\'), curFile);
4240
				setInnerHTML(document.getElementById(\'item_count\'), totalItems);';
4241
		}
4242
4243
		echo '
4244
				// Is there an error?
4245
				if (oXMLDoc.getElementsByTagName("error")[0])
4246
				{
4247
					var sErrorMsg = "";
4248
					for (var i = 0; i < oXMLDoc.getElementsByTagName("error")[0].childNodes.length; i++)
4249
						sErrorMsg += oXMLDoc.getElementsByTagName("error")[0].childNodes[i].nodeValue;
4250
					document.getElementById("error_block").style.display = "";
4251
					setInnerHTML(document.getElementById("error_message"), sErrorMsg);
4252
					return false;
4253
				}
4254
4255
				// Get the progress bar right.
4256
				barTotal = debugItems * ', $upcontext['file_count'], ';
4257
				barDone = (debugItems * (curFile - 1)) + lastItem;
4258
4259
				updateStepProgress(barDone, barTotal, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4260
4261
				// Finally - update the time here as it shows the server is responding!
4262
				curTime = new Date();
4263
				iElapsed = (curTime.getTime() / 1000 - ', $upcontext['started'], ');
4264
				mins = parseInt(iElapsed / 60);
4265
				secs = parseInt(iElapsed - mins * 60);
4266
				setInnerHTML(document.getElementById("mins_elapsed"), mins);
4267
				setInnerHTML(document.getElementById("secs_elapsed"), secs);
4268
4269
				getNextItem();
4270
				return true;
4271
			}
4272
4273
			// What if we timeout?!
4274
			function retTimeout(attemptAgain)
4275
			{
4276
				// Oh noes...
4277
				if (!attemptAgain)
4278
				{
4279
					document.getElementById("error_block").style.display = "";
4280
					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");
4281
				}
4282
				else
4283
				{
4284
					document.getElementById("error_block").style.display = "none";
4285
					getNextItem();
4286
				}
4287
			}';
4288
4289
		// Start things off assuming we've not errored.
4290
		if (empty($upcontext['error_message']))
4291
			echo '
4292
			getNextItem();';
4293
4294
		echo '
4295
		//# sourceURL=dynamicScript-dbch.js
4296
		</script>';
4297
	}
4298
	return;
4299
}
4300
4301
function template_database_xml()
4302
{
4303
	global $is_debug, $upcontext;
4304
4305
	echo '
4306
	<file num="', $upcontext['cur_file_num'], '" items="', $upcontext['total_items'], '" debug_items="', $upcontext['debug_items'], '">', $upcontext['cur_file_name'], '</file>
4307
	<item num="', $upcontext['current_item_num'], '">', $upcontext['current_item_name'], '</item>
4308
	<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>';
4309
4310
	if (!empty($upcontext['error_message']))
4311
		echo '
4312
	<error>', $upcontext['error_message'], '</error>';
4313
4314
	if (!empty($upcontext['error_string']))
4315
		echo '
4316
	<sql>', $upcontext['error_string'], '</sql>';
4317
4318
	if ($is_debug)
4319
		echo '
4320
	<curtime>', time(), '</curtime>';
4321
}
4322
4323
// Template for the UTF-8 conversion step. Basically a copy of the backup stuff with slight modifications....
4324
function template_convert_utf8()
4325
{
4326
	global $upcontext, $support_js, $is_debug;
4327
4328
	echo '
4329
			<h3>Please wait while your database is converted to UTF-8. For large forums this may take some time!</h3>';
4330
4331
	echo '
4332
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4333
			<input type="hidden" name="utf8_done" id="utf8_done" value="0">
4334
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
4335
			<div id="debug_section" style="height: ', ($is_debug ? '97' : '12') , 'px; overflow: auto;">
4336
			<span id="debuginfo"></span>
4337
			</div>';
4338
4339
	// Done any tables so far?
4340
	if (!empty($upcontext['previous_tables']))
4341
		foreach ($upcontext['previous_tables'] as $table)
4342
			echo '
4343
			<br>Completed Table: &quot;', $table, '&quot;.';
4344
4345
	echo '
4346
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>';
4347
4348
	// If we dropped their index, let's let them know
4349
	if ($upcontext['dropping_index'])
4350
		echo '
4351
				<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>';
4352
4353
	// Completion notification
4354
	echo '
4355
			<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>';
4356
4357
	// Continue please!
4358
	$upcontext['continue'] = $support_js ? 2 : 1;
4359
4360
	// If javascript allows we want to do this using XML.
4361
	if ($support_js)
4362
	{
4363
		echo '
4364
		<script>
4365
			var lastTable = ', $upcontext['cur_table_num'], ';
4366
			function getNextTables()
4367
			{
4368
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onConversionUpdate);
4369
			}
4370
4371
			// Got an update!
4372
			function onConversionUpdate(oXMLDoc)
4373
			{
4374
				var sCurrentTableName = "";
4375
				var iTableNum = 0;
4376
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4377
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4378
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4379
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4380
4381
				// Update the page.
4382
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4383
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4384
				lastTable = iTableNum;
4385
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4386
4387
		// If debug flood the screen.
4388
		if ($is_debug)
4389
			echo '
4390
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4391
4392
				if (document.getElementById(\'debug_section\').scrollHeight)
4393
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4394
4395
		echo '
4396
				// Get the next update...
4397
				if (iTableNum == ', $upcontext['table_count'], ')
4398
				{
4399
					document.getElementById(\'commess\').style.display = "";
4400
					if (document.getElementById(\'indexmsg\') != null) {
4401
						document.getElementById(\'indexmsg\').style.display = "";
4402
					}
4403
					document.getElementById(\'current_tab_div\').style.display = "none";
4404
					document.getElementById(\'contbutt\').disabled = 0;
4405
					document.getElementById(\'utf8_done\').value = 1;
4406
				}
4407
				else
4408
					getNextTables();
4409
			}
4410
			getNextTables();
4411
		//# sourceURL=dynamicScript-conv.js
4412
		</script>';
4413
	}
4414
}
4415
4416
function template_convert_xml()
4417
{
4418
	global $upcontext;
4419
4420
	echo '
4421
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4422
}
4423
4424
// Template for the database backup tool/
4425
function template_serialize_json()
4426
{
4427
	global $upcontext, $support_js, $is_debug;
4428
4429
	echo '
4430
			<h3>Converting data from serialize to JSON...</h3>';
4431
4432
	echo '
4433
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4434
			<input type="hidden" name="json_done" id="json_done" value="0">
4435
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
4436
			<div id="debug_section" style="height: ', ($is_debug ? '115' : '12') , 'px; overflow: auto;">
4437
			<span id="debuginfo"></span>
4438
			</div>';
4439
4440
	// Dont any tables so far?
4441
	if (!empty($upcontext['previous_tables']))
4442
		foreach ($upcontext['previous_tables'] as $table)
4443
			echo '
4444
			<br>Completed Table: &quot;', $table, '&quot;.';
4445
4446
	echo '
4447
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
4448
			<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>';
4449
4450
	// Try to make sure substep was reset.
4451
	if ($upcontext['cur_table_num'] == $upcontext['table_count'])
4452
		echo '
4453
			<input type="hidden" name="substep" id="substep" value="0">';
4454
4455
	// Continue please!
4456
	$upcontext['continue'] = $support_js ? 2 : 1;
4457
4458
	// If javascript allows we want to do this using XML.
4459
	if ($support_js)
4460
	{
4461
		echo '
4462
		<script>
4463
			var lastTable = ', $upcontext['cur_table_num'], ';
4464
			function getNextTables()
4465
			{
4466
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4467
			}
4468
4469
			// Got an update!
4470
			function onBackupUpdate(oXMLDoc)
4471
			{
4472
				var sCurrentTableName = "";
4473
				var iTableNum = 0;
4474
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4475
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4476
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4477
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4478
4479
				// Update the page.
4480
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4481
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4482
				lastTable = iTableNum;
4483
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4484
4485
		// If debug flood the screen.
4486
		if ($is_debug)
4487
			echo '
4488
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4489
4490
				if (document.getElementById(\'debug_section\').scrollHeight)
4491
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4492
4493
		echo '
4494
				// Get the next update...
4495
				if (iTableNum == ', $upcontext['table_count'], ')
4496
				{
4497
					document.getElementById(\'commess\').style.display = "";
4498
					document.getElementById(\'current_tab_div\').style.display = "none";
4499
					document.getElementById(\'contbutt\').disabled = 0;
4500
					document.getElementById(\'json_done\').value = 1;
4501
				}
4502
				else
4503
					getNextTables();
4504
			}
4505
			getNextTables();
4506
		//# sourceURL=dynamicScript-json.js
4507
		</script>';
4508
	}
4509
}
4510
4511
function template_serialize_json_xml()
4512
{
4513
	global $upcontext;
4514
4515
	echo '
4516
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4517
}
4518
4519
function template_upgrade_complete()
4520
{
4521
	global $upcontext, $upgradeurl, $settings, $boardurl, $is_debug;
4522
4523
	echo '
4524
	<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>
4525
	<form action="', $boardurl, '/index.php">';
4526
4527
	if (!empty($upcontext['can_delete_script']))
4528
		echo '
4529
			<label for="delete_self"><input type="checkbox" id="delete_self" onclick="doTheDelete(this);"> Delete upgrade.php and its data files now</label> <em>(doesn\'t work on all servers).</em>
4530
			<script>
4531
				function doTheDelete(theCheck)
4532
				{
4533
					var theImage = document.getElementById ? document.getElementById("delete_upgrader") : document.all.delete_upgrader;
4534
4535
					theImage.src = "', $upgradeurl, '?delete=1&ts_" + (new Date().getTime());
4536
					theCheck.disabled = true;
4537
				}
4538
			</script>
4539
			<img src="', $settings['default_theme_url'], '/images/blank.png" alt="" id="delete_upgrader"><br>';
4540
4541
	$active = time() - $upcontext['started'];
4542
	$hours = floor($active / 3600);
4543
	$minutes = intval(($active / 60) % 60);
4544
	$seconds = intval($active % 60);
4545
4546
	if ($is_debug)
4547
	{
4548
		$totalTime = '';
4549
		if ($hours > 0)
4550
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
4551
		if ($minutes > 0)
4552
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
4553
		if ($seconds > 0)
4554
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
4555
	}
4556
4557
	if ($is_debug && !empty($totalTime))
4558
		echo '<br> Upgrade completed in ', $totalTime, '<br><br>';
4559
4560
	echo '<br>
4561
			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>
4562
			<br>
4563
			Best of luck,<br>
4564
			Simple Machines';
4565
}
4566
4567
/**
4568
 * Convert MySQL (var)char ip col to binary
4569
 *
4570
 * @param string $targetTable The table to perform the operation on
4571
 * @param string $oldCol The old column to gather data from
4572
 * @param string $newCol The new column to put data in
4573
 * @param int $limit The amount of entries to handle at once.
4574
 * @param int $setSize The amount of entries after which to update the database.
4575
 *
4576
 * newCol needs to be a varbinary(16) null able field
4577
 * @return bool
4578
 */
4579
function MySQLConvertOldIp($targetTable, $oldCol, $newCol, $limit = 50000, $setSize = 100)
4580
{
4581
	global $smcFunc, $step_progress;
4582
4583
	$current_substep = $_GET['substep'];
4584
4585
	if (empty($_GET['a']))
4586
		$_GET['a'] = 0;
4587
	$step_progress['name'] = 'Converting ips';
4588
	$step_progress['current'] = $_GET['a'];
4589
4590
	// Skip this if we don't have the column
4591
	$request = $smcFunc['db_query']('', '
4592
		SHOW FIELDS
4593
		FROM {db_prefix}{raw:table}
4594
		WHERE Field = {string:name}',
4595
		array(
4596
			'table' => $targetTable,
4597
			'name' => $oldCol,
4598
	));
4599
	if ($smcFunc['db_num_rows']($request) !== 1)
4600
	{
4601
		$smcFunc['db_free_result']($request);
4602
		return;
4603
	}
4604
	$smcFunc['db_free_result']($request);
4605
4606
	//mysql default max length is 1mb https://dev.mysql.com/doc/refman/5.1/en/packet-too-large.html
4607
	$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...
4608
4609
	$is_done = false;
4610
	while (!$is_done)
4611
	{
4612
		// Keep looping at the current step.
4613
		nextSubStep($current_substep);
4614
4615
		$arIp = array();
4616
4617
		$request = $smcFunc['db_query']('', '
4618
			SELECT DISTINCT {raw:old_col}
4619
			FROM {db_prefix}{raw:table_name}
4620
			WHERE {raw:new_col} IS NULL
4621
			LIMIT {int:limit}',
4622
			array(
4623
				'old_col' => $oldCol,
4624
				'new_col' => $newCol,
4625
				'table_name' => $targetTable,
4626
				'empty' => '',
4627
				'limit' => $limit,
4628
		));
4629
		while ($row = $smcFunc['db_fetch_assoc']($request))
4630
			$arIp[] = $row[$oldCol];
4631
		$smcFunc['db_free_result']($request);
4632
4633
		// Special case, null ip could keep us in a loop.
4634
		if (is_null($arIp[0]))
4635
			unset($arIp[0]);
4636
4637
		if (empty($arIp))
4638
			$is_done = true;
4639
4640
		$updates = array();
4641
		$cases = array();
4642
		$count = count($arIp);
4643
		for ($i = 0; $i < $count; $i++)
4644
		{
4645
			$arIp[$i] = trim($arIp[$i]);
4646
4647
			if (empty($arIp[$i]))
4648
				continue;
4649
4650
			$updates['ip' . $i] = $arIp[$i];
4651
			$cases[$arIp[$i]] = 'WHEN ' . $oldCol . ' = {string:ip' . $i . '} THEN {inet:ip' . $i . '}';
4652
4653
			if ($setSize > 0 && $i % $setSize === 0)
4654
			{
4655
				if (count($updates) == 1)
4656
					continue;
4657
4658
				$updates['whereSet'] = array_values($updates);
4659
				$smcFunc['db_query']('', '
4660
					UPDATE {db_prefix}' . $targetTable . '
4661
					SET ' . $newCol . ' = CASE ' .
4662
					implode('
4663
						', $cases) . '
4664
						ELSE NULL
4665
					END
4666
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4667
					$updates
4668
				);
4669
4670
				$updates = array();
4671
				$cases = array();
4672
			}
4673
		}
4674
4675
		// Incase some extras made it through.
4676
		if (!empty($updates))
4677
		{
4678
			if (count($updates) == 1)
4679
			{
4680
				foreach ($updates as $key => $ip)
4681
				{
4682
					$smcFunc['db_query']('', '
4683
						UPDATE {db_prefix}' . $targetTable . '
4684
						SET ' . $newCol . ' = {inet:ip}
4685
						WHERE ' . $oldCol . ' = {string:ip}',
4686
						array(
4687
							'ip' => $ip
4688
					));
4689
				}
4690
			}
4691
			else
4692
			{
4693
				$updates['whereSet'] = array_values($updates);
4694
				$smcFunc['db_query']('', '
4695
					UPDATE {db_prefix}' . $targetTable . '
4696
					SET ' . $newCol . ' = CASE ' .
4697
					implode('
4698
						', $cases) . '
4699
						ELSE NULL
4700
					END
4701
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4702
					$updates
4703
				);
4704
			}
4705
		}
4706
		else
4707
			$is_done = true;
4708
4709
		$_GET['a'] += $limit;
4710
		$step_progress['current'] = $_GET['a'];
4711
	}
4712
4713
	unset($_GET['a']);
4714
}
4715
4716
/**
4717
 * Get the column info. This is basically the same as smf_db_list_columns but we get 1 column, force detail and other checks.
4718
 *
4719
 * @param string $targetTable The table to perform the operation on
4720
 * @param string $column The column we are looking for.
4721
 *
4722
 * @return array Info on the table.
4723
 */
4724
function upgradeGetColumnInfo($targetTable, $column)
4725
{
4726
 	global $smcFunc;
4727
4728
 	// This should already be here, but be safe.
4729
 	db_extend('packages');
4730
4731
 	$columns = $smcFunc['db_list_columns']($targetTable, true);
4732
4733
	if (isset($columns[$column]))
4734
		return $columns[$column];
4735
	else
4736
		return null;
4737
}
4738
4739
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

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

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

Loading history...