Completed
Pull Request — release-2.1 (#3761)
by John
08:15
created

upgrade.php ➔ ConvertUtf8()   F

Complexity

Conditions 55
Paths > 20000

Size

Total Lines 483
Code Lines 302

Duplication

Lines 23
Ratio 4.76 %

Importance

Changes 0
Metric Value
cc 55
eloc 302
nc 3879937
nop 0
dl 23
loc 483
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines http://www.simplemachines.org
8
 * @copyright 2017 Simple Machines and individual contributors
9
 * @license http://www.simplemachines.org/about/smf/license.php BSD
10
 *
11
 * @version 2.1 Beta 3
12
 */
13
14
// Version information...
15
define('SMF_VERSION', '2.1 Beta 3');
16
define('SMF_LANG_VERSION', '2.1 Beta 3');
17
18
/**
19
 * The minimum required PHP version.
20
 * @var string
21
 */
22
$GLOBALS['required_php_version'] = '5.3.8';
23
24
/**
25
 * A list of supported database systems.
26
 * @var array
27
 */
28
$databases = array(
29
	'mysql' => array(
30
		'name' => 'MySQL',
31
		'version' => '5.0.3',
32
		'version_check' => 'global $db_connection; return min(mysqli_get_server_info($db_connection), mysqli_get_client_info());',
33
		'utf8_support' => true,
34
		'utf8_version' => '5.0.3',
35
		'utf8_version_check' => 'global $db_connection; return mysqli_get_server_info($db_connection);',
36
		'alter_support' => true,
37
	),
38
	'postgresql' => array(
39
		'name' => 'PostgreSQL',
40
		'version' => '9.1',
41
		'version_check' => '$version = pg_version(); return $version[\'client\'];',
42
		'always_has_db' => true,
43
	),
44
);
45
46
/**
47
 * The maximum time a single substep may take, in seconds.
48
 * @var int
49
 */
50
$timeLimitThreshold = 3;
51
52
/**
53
 * The current path to the upgrade.php file.
54
 * @var string
55
 */
56
$upgrade_path = dirname(__FILE__);
57
58
/**
59
 * The URL of the current page.
60
 * @var string
61
 */
62
$upgradeurl = $_SERVER['PHP_SELF'];
63
64
/**
65
 * The base URL for the external SMF resources.
66
 * @var string
67
 */
68
$smfsite = 'http://www.simplemachines.org/smf';
69
70
/**
71
 * Flag to disable the required administrator login.
72
 * @var bool
73
 */
74
$disable_security = false;
75
76
/**
77
 * The amount of seconds allowed between logins.
78
 * If the first user to login is inactive for this amount of seconds, a second login is allowed.
79
 * @var int
80
 */
81
$upcontext['inactive_timeout'] = 10;
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
// Are we logged in?
123
if (isset($upgradeData))
124
{
125
	$upcontext['user'] = unserialize(base64_decode($upgradeData));
126
127
	// Check for sensible values.
128 View Code Duplication
	if (empty($upcontext['user']['started']) || $upcontext['user']['started'] < time() - 86400)
129
		$upcontext['user']['started'] = time();
130 View Code Duplication
	if (empty($upcontext['user']['updated']) || $upcontext['user']['updated'] < time() - 86400)
131
		$upcontext['user']['updated'] = 0;
132
133
	$upcontext['started'] = $upcontext['user']['started'];
134
	$upcontext['updated'] = $upcontext['user']['updated'];
135
136
	$is_debug = !empty($upcontext['user']['debug']) ? true : false;
137
}
138
139
// Nothing sensible?
140
if (empty($upcontext['updated']))
141
{
142
	$upcontext['started'] = time();
143
	$upcontext['updated'] = 0;
144
	$upcontext['user'] = array(
145
		'id' => 0,
146
		'name' => 'Guest',
147
		'pass' => 0,
148
		'started' => $upcontext['started'],
149
		'updated' => $upcontext['updated'],
150
	);
151
}
152
153
// Load up some essential data...
154
loadEssentialData();
155
156
// Are we going to be mimic'ing SSI at this point?
157
if (isset($_GET['ssi']))
158
{
159
	require_once($sourcedir . '/Errors.php');
160
	require_once($sourcedir . '/Logging.php');
161
	require_once($sourcedir . '/Load.php');
162
	require_once($sourcedir . '/Security.php');
163
	require_once($sourcedir . '/Subs-Package.php');
164
165
	loadUserSettings();
166
	loadPermissions();
167
}
168
169
// Include our helper functions.
170
require_once($sourcedir . '/Subs.php');
171
require_once($sourcedir . '/LogInOut.php');
172
require_once($upgrade_path . '/upgrade-helper.php');
173
174
// This only exists if we're on SMF ;)
175
if (isset($modSettings['smfVersion']))
176
{
177
	$request = $smcFunc['db_query']('', '
178
		SELECT variable, value
179
		FROM {db_prefix}themes
180
		WHERE id_theme = {int:id_theme}
181
			AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
182
		array(
183
			'id_theme' => 1,
184
			'theme_url' => 'theme_url',
185
			'theme_dir' => 'theme_dir',
186
			'images_url' => 'images_url',
187
			'db_error_skip' => true,
188
		)
189
	);
190 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
191
		$modSettings[$row['variable']] = $row['value'];
192
	$smcFunc['db_free_result']($request);
193
}
194
195
if (!isset($modSettings['theme_url']))
196
{
197
	$modSettings['theme_dir'] = $boarddir . '/Themes/default';
198
	$modSettings['theme_url'] = 'Themes/default';
199
	$modSettings['images_url'] = 'Themes/default/images';
200
}
201
if (!isset($settings['default_theme_url']))
202
	$settings['default_theme_url'] = $modSettings['theme_url'];
203
if (!isset($settings['default_theme_dir']))
204
	$settings['default_theme_dir'] = $modSettings['theme_dir'];
205
206
$upcontext['is_large_forum'] = (empty($modSettings['smfVersion']) || $modSettings['smfVersion'] <= '1.1 RC1') && !empty($modSettings['totalMessages']) && $modSettings['totalMessages'] > 75000;
207
// Default title...
208
$upcontext['page_title'] = 'Updating Your SMF Installation!';
209
210
// Have we got tracking data - if so use it (It will be clean!)
211
if (isset($_GET['data']))
212
{
213
	global $is_debug;
214
215
	$upcontext['upgrade_status'] = safe_unserialize(base64_decode($_GET['data']));
216
	$upcontext['current_step'] = $upcontext['upgrade_status']['curstep'];
217
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
218
	$upcontext['rid'] = $upcontext['upgrade_status']['rid'];
219
	$support_js = $upcontext['upgrade_status']['js'];
220
221
	// Only set this if the upgrader status says so.
222
	if (empty($is_debug))
223
		$is_debug = $upcontext['upgrade_status']['debug'];
224
225
	// Load the language.
226
	if (file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
227
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
228
}
229
// Set the defaults.
230
else
231
{
232
	$upcontext['current_step'] = 0;
233
	$upcontext['rid'] = mt_rand(0, 5000);
234
	$upcontext['upgrade_status'] = array(
235
		'curstep' => 0,
236
		'lang' => isset($_GET['lang']) ? $_GET['lang'] : basename($language, '.lng'),
237
		'rid' => $upcontext['rid'],
238
		'pass' => 0,
239
		'debug' => 0,
240
		'js' => 0,
241
	);
242
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
243
}
244
245
// If this isn't the first stage see whether they are logging in and resuming.
246
if ($upcontext['current_step'] != 0 || !empty($upcontext['user']['step']))
247
	checkLogin();
248
249
if ($command_line)
250
	cmdStep0();
251
252
// Don't error if we're using xml.
253
if (isset($_GET['xml']))
254
	$upcontext['return_error'] = true;
255
256
// Loop through all the steps doing each one as required.
257
$upcontext['overall_percent'] = 0;
258
foreach ($upcontext['steps'] as $num => $step)
259
{
260
	if ($num >= $upcontext['current_step'])
261
	{
262
		// The current weight of this step in terms of overall progress.
263
		$upcontext['step_weight'] = $step[3];
264
		// Make sure we reset the skip button.
265
		$upcontext['skip'] = false;
266
267
		// We cannot proceed if we're not logged in.
268
		if ($num != 0 && !$disable_security && $upcontext['user']['pass'] != $upcontext['upgrade_status']['pass'])
269
		{
270
			$upcontext['steps'][0][2]();
271
			break;
272
		}
273
274
		// Call the step and if it returns false that means pause!
275 View Code Duplication
		if (function_exists($step[2]) && $step[2]() === false)
276
			break;
277
		elseif (function_exists($step[2]))
278
			$upcontext['current_step']++;
279
	}
280
	$upcontext['overall_percent'] += $step[3];
281
}
282
283
upgradeExit();
284
285
// Exit the upgrade script.
286
function upgradeExit($fallThrough = false)
287
{
288
	global $upcontext, $upgradeurl, $sourcedir, $command_line, $is_debug;
289
290
	// Save where we are...
291
	if (!empty($upcontext['current_step']) && !empty($upcontext['user']['id']))
292
	{
293
		$upcontext['user']['step'] = $upcontext['current_step'];
294
		$upcontext['user']['substep'] = $_GET['substep'];
295
		$upcontext['user']['updated'] = time();
296
		$upcontext['debug'] = $is_debug;
297
		$upgradeData = base64_encode(safe_serialize($upcontext['user']));
298
		require_once($sourcedir . '/Subs-Admin.php');
299
		updateSettingsFile(array('upgradeData' => '"' . $upgradeData . '"'));
300
		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...
301
	}
302
303
	// Handle the progress of the step, if any.
304
	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
305
	{
306
		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
307
		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
308
	}
309
	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
310
311
	// We usually dump our templates out.
312
	if (!$fallThrough)
313
	{
314
		// This should not happen my dear... HELP ME DEVELOPERS!!
315
		if (!empty($command_line))
316
		{
317
			if (function_exists('debug_print_backtrace'))
318
				debug_print_backtrace();
319
320
			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.';
0 ignored issues
show
Security Cross-Site Scripting introduced by
' ' . 'Error: Unexpected... up and running again.' can contain request data and is used in output context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 294
  2. $upcontext is assigned
    in other/upgrade.php on line 295
  3. $upcontext is assigned
    in other/upgrade.php on line 296
  4. $upcontext is assigned
    in other/upgrade.php on line 309

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
321
			flush();
322
			die();
323
		}
324
325
		if (!isset($_GET['xml']))
326
			template_upgrade_above();
327
		else
328
		{
329
			header('Content-Type: text/xml; charset=UTF-8');
330
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
331
			$upcontext['get_data'] = array();
332
			foreach ($_GET as $k => $v)
333
			{
334
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
335
				{
336
					$upcontext['get_data'][$k] = $v;
337
				}
338
			}
339
			template_xml_above();
340
		}
341
342
		// Call the template.
343
		if (isset($upcontext['sub_template']))
344
		{
345
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
346
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(safe_serialize($upcontext['upgrade_status']));
347
348
			// Custom stuff to pass back?
349
			if (!empty($upcontext['query_string']))
350
				$upcontext['form_url'] .= $upcontext['query_string'];
351
352
			call_user_func('template_' . $upcontext['sub_template']);
0 ignored issues
show
Security Code Execution introduced by
'template_' . $upcontext['sub_template'] can contain request data and is used in code execution context(s) leading to a potential security vulnerability.

3 paths for user data to reach this point

  1. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 294
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 294
  2. $upcontext is assigned
    in other/upgrade.php on line 295
  3. $upcontext is assigned
    in other/upgrade.php on line 296
  4. $upcontext is assigned
    in other/upgrade.php on line 309
  5. $upcontext is assigned
    in other/upgrade.php on line 345
  6. $upcontext is assigned
    in other/upgrade.php on line 346
  2. Path: Read from $_GET, and $v is assigned in other/upgrade.php on line 332
  1. Read from $_GET, and $v is assigned
    in other/upgrade.php on line 332
  2. $upcontext is assigned
    in other/upgrade.php on line 336
  3. $upcontext is assigned
    in other/upgrade.php on line 345
  4. $upcontext is assigned
    in other/upgrade.php on line 346
  3. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 346
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 346

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
353
		}
354
355
		// Was there an error?
356
		if (!empty($upcontext['forced_error_message']))
357
			echo $upcontext['forced_error_message'];
0 ignored issues
show
Security Cross-Site Scripting introduced by
$upcontext['forced_error_message'] can contain request data and is used in output context(s) leading to a potential security vulnerability.

3 paths for user data to reach this point

  1. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 294
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 294
  2. $upcontext is assigned
    in other/upgrade.php on line 295
  3. $upcontext is assigned
    in other/upgrade.php on line 296
  4. $upcontext is assigned
    in other/upgrade.php on line 309
  2. Path: Read from $_GET, and $v is assigned in other/upgrade.php on line 332
  1. Read from $_GET, and $v is assigned
    in other/upgrade.php on line 332
  2. $upcontext is assigned
    in other/upgrade.php on line 336
  3. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 346
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 346

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
358
359
		// Show the footer.
360
		if (!isset($_GET['xml']))
361
			template_upgrade_below();
362
		else
363
			template_xml_below();
364
	}
365
366
367
	if (!empty($command_line) && $is_debug)
368
	{
369
		$active = time() - $upcontext['started'];
370
		$hours = floor($active / 3600);
371
		$minutes = intval(($active / 60) % 60);
372
		$seconds = intval($active % 60);
373
374
		$totalTime = '';
375
		if ($hours > 0)
376
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
377
		if ($minutes > 0)
378
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
379
		if ($seconds > 0)
380
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
381
382
		if (!empty($totalTime))
383
			echo "\n" . 'Upgrade completed in ' . $totalTime . "\n";
384
	}
385
386
	// Bang - gone!
387
	die();
388
}
389
390
// Used to direct the user to another location.
391
function redirectLocation($location, $addForm = true)
392
{
393
	global $upgradeurl, $upcontext, $command_line;
394
395
	// Command line users can't be redirected.
396
	if ($command_line)
397
		upgradeExit(true);
398
399
	// Are we providing the core info?
400
	if ($addForm)
401
	{
402
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
403
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(safe_serialize($upcontext['upgrade_status'])) . $location;
404
	}
405
406
	while (@ob_end_clean());
407
	header('Location: ' . strtr($location, array('&amp;' => '&')));
0 ignored issues
show
Security Response Splitting introduced by
'Location: ' . strtr($lo... array('&amp;' => '&')) can contain request data and is used in response header context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_GET, and $location is assigned
    in other/upgrade.php on line 403
  2. $location is passed through strtr()
    in other/upgrade.php on line 407

Response Splitting Attacks

Allowing an attacker to set a response header, opens your application to response splitting attacks; effectively allowing an attacker to send any response, he would like.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
408
409
	// Exit - saving status as we go.
410
	upgradeExit(true);
411
}
412
413
// Load all essential data and connect to the DB as this is pre SSI.php
414
function loadEssentialData()
415
{
416
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection, $db_prefix, $db_character_set, $db_type;
417
	global $modSettings, $sourcedir, $smcFunc;
418
419
	// Do the non-SSI stuff...
420
	if (function_exists('set_magic_quotes_runtime'))
421
		@set_magic_quotes_runtime(0);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
422
423
	error_reporting(E_ALL);
424
	define('SMF', 1);
425
426
	// Start the session.
427
	if (@ini_get('session.save_handler') == 'user')
428
		@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...
429
	@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...
430
431
	if (empty($smcFunc))
432
		$smcFunc = array();
433
434
	// We need this for authentication and some upgrade code
435
	require_once($sourcedir . '/Subs-Auth.php');
436
	require_once($sourcedir . '/Class-Package.php');
437
438
	$smcFunc['strtolower'] = 'smf_strtolower';
439
440
	// Initialize everything...
441
	initialize_inputs();
442
443
	// Get the database going!
444
	if (empty($db_type))
445
		$db_type = 'mysql';
446
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
447
	{
448
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
449
450
		// Make the connection...
451
		$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, array('non_fatal' => true));
452
453
		// Oh dear god!!
454
		if ($db_connection === null)
455
			die('Unable to connect to database - please check username and password are correct in Settings.php');
456
457
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
458
			$smcFunc['db_query']('', '
459
			SET NAMES {string:db_character_set}',
460
			array(
461
				'db_error_skip' => true,
462
				'db_character_set' => $db_character_set,
463
			)
464
		);
465
466
		// Load the modSettings data...
467
		$request = $smcFunc['db_query']('', '
468
			SELECT variable, value
469
			FROM {db_prefix}settings',
470
			array(
471
				'db_error_skip' => true,
472
			)
473
		);
474
		$modSettings = array();
475 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
476
			$modSettings[$row['variable']] = $row['value'];
477
		$smcFunc['db_free_result']($request);
478
	}
479
	else
480
	{
481
		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.');
482
	}
483
484
	require_once($sourcedir . '/Subs.php');
485
486
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
487
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
488
	{
489
		require_once($sourcedir . '/QueryString.php');
490
		cleanRequest();
491
	}
492
493
	if (!isset($_GET['substep']))
494
		$_GET['substep'] = 0;
495
}
496
497
function initialize_inputs()
498
{
499
	global $start_time, $upcontext, $db_type;
500
501
	$start_time = time();
502
503
	umask(0);
504
505
	ob_start();
506
507
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
508
	ignore_user_abort(true);
509
510
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
511
	if (isset($_GET['delete']))
512
	{
513
		@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...
514
515
		// And the extra little files ;).
516
		@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...
517
		@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...
518
		@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...
519
		@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...
520
		@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...
521
522
		$dh = opendir(dirname(__FILE__));
523
		while ($file = readdir($dh))
524
		{
525
			if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
526
				@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...
527
		}
528
		closedir($dh);
529
530
		// Legacy files while we're at it. NOTE: We only touch files we KNOW shouldn't be there.
531
		// 1.1 Sources files not in 2.0+
532
		@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...
533
		// 1.1 Templates that don't exist any more (e.g. renamed)
534
		@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...
535
		@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...
536
		// 1.1 JS files were stored in the main theme folder, but in 2.0+ are in the scripts/ folder
537
		@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...
538
		@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...
539
		@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...
540
		@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...
541
		@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...
542
543
		// 2.0 Sources files not in 2.1+
544
		@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...
545
		@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...
546
547
		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');
0 ignored issues
show
Security Response Splitting introduced by
'Location: http://' . (i...fault/images/blank.png' can contain request data and is used in response header context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Fetching key HTTP_HOST from $_SERVER
    in other/upgrade.php on line 547

Response Splitting Attacks

Allowing an attacker to set a response header, opens your application to response splitting attacks; effectively allowing an attacker to send any response, he would like.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
548
		exit;
549
	}
550
551
	// Anybody home?
552 View Code Duplication
	if (!isset($_GET['xml']))
553
	{
554
		$upcontext['remote_files_available'] = false;
555
		$test = @fsockopen('www.simplemachines.org', 80, $errno, $errstr, 1);
556
		if ($test)
557
			$upcontext['remote_files_available'] = true;
558
		@fclose($test);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
559
	}
560
561
	// Something is causing this to happen, and it's annoying.  Stop it.
562
	$temp = 'upgrade_php?step';
563
	while (strlen($temp) > 4)
564
	{
565
		if (isset($_GET[$temp]))
566
			unset($_GET[$temp]);
567
		$temp = substr($temp, 1);
568
	}
569
570
	// Force a step, defaulting to 0.
571
	$_GET['step'] = (int) @$_GET['step'];
572
	$_GET['substep'] = (int) @$_GET['substep'];
573
}
574
575
// Step 0 - Let's welcome them in and ask them to login!
576
function WelcomeLogin()
577
{
578
	global $boarddir, $sourcedir, $modSettings, $cachedir, $upgradeurl, $upcontext;
579
	global $smcFunc, $db_type, $databases, $boardurl;
580
581
	// We global $txt here so that the language files can add to them. This variable is NOT unused.
582
	global $txt;
583
584
	$upcontext['sub_template'] = 'welcome_message';
585
586
	// Check for some key files - one template, one language, and a new and an old source file.
587
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
588
		&& @file_exists($sourcedir . '/QueryString.php')
589
		&& @file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')
590
		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
591
592
	// Need legacy scripts?
593 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.1)
594
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
595 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.0)
596
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-1.sql');
597 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 1.1)
598
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
599
600
	// This needs to exist!
601
	if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
602
		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>]');
603
	else
604
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
605
606
	if (!$check)
607
		// 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.
608
		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.');
609
610
	// Do they meet the install requirements?
611
	if (!php_version_check())
612
		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.');
613
614
	if (!db_version_check())
615
		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.');
616
617
	// Do some checks to make sure they have proper privileges
618
	db_extend('packages');
619
620
	// CREATE
621
	$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');
622
623
	// ALTER
624
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
625
626
	// DROP
627
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
628
629
	// Sorry... we need CREATE, ALTER and DROP
630 View Code Duplication
	if (!$create || !$alter || !$drop)
631
		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.');
632
633
	// Do a quick version spot check.
634
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
635
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
636
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
637
		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.');
638
639
	// What absolutely needs to be writable?
640
	$writable_files = array(
641
		$boarddir . '/Settings.php',
642
		$boarddir . '/Settings_bak.php',
643
		$boarddir . '/db_last_error.php',
644
		$modSettings['theme_dir'] . '/css/minified.css',
645
		$modSettings['theme_dir'] . '/scripts/minified.js',
646
		$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
647
	);
648
649
	// Do we need to add this setting?
650
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
651
652
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] . '/custom_avatar';
653
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
654
655
	// This little fellow has to cooperate...
656
	quickFileWritable($custom_av_dir);
657
658
	// Are we good now?
659
	if (!is_writable($custom_av_dir))
660
		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));
661
	elseif ($need_settings_update)
662
	{
663
		if (!function_exists('cache_put_data'))
664
			require_once($sourcedir . '/Load.php');
665
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
666
		updateSettings(array('custom_avatar_url' => $custom_av_url));
667
	}
668
669
	require_once($sourcedir . '/Security.php');
670
671
	// Check the cache directory.
672
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
673
	if (!file_exists($cachedir_temp))
674
		@mkdir($cachedir_temp);
675
	if (!file_exists($cachedir_temp))
676
		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.');
677
678
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
679
		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>.');
680
	elseif (!isset($_GET['skiplang']))
681
	{
682
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
683
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
684
685
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
686
			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>]');
687
	}
688
689
	if (!makeFilesWritable($writable_files))
690
		return false;
691
692
	// Check agreement.txt. (it may not exist, in which case $boarddir must be writable.)
693 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
694
		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.');
695
696
	// Upgrade the agreement.
697
	elseif (isset($modSettings['agreement']))
698
	{
699
		$fp = fopen($boarddir . '/agreement.txt', 'w');
700
		fwrite($fp, $modSettings['agreement']);
701
		fclose($fp);
702
	}
703
704
	// We're going to check that their board dir setting is right in case they've been moving stuff around.
705
	if (strtr($boarddir, array('/' => '', '\\' => '')) != strtr(dirname(__FILE__), array('/' => '', '\\' => '')))
706
		$upcontext['warning'] = '
707
			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>
708
			<ul>
709
				<li>Board Directory: ' . $boarddir . '</li>
710
				<li>Source Directory: ' . $boarddir . '</li>
711
				<li>Cache Directory: ' . $cachedir_temp . '</li>
712
			</ul>
713
			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="http://download.simplemachines.org/?tools">Repair Settings</a> tool from the Simple Machines website before continuing.';
714
715
	// Either we're logged in or we're going to present the login.
716
	if (checkLogin())
717
		return true;
718
719
	$upcontext += createToken('login');
720
721
	return false;
722
}
723
724
// Step 0.5: Does the login work?
725
function checkLogin()
726
{
727
	global $modSettings, $upcontext, $disable_security;
728
	global $smcFunc, $db_type, $support_js;
729
730
	// Don't bother if the security is disabled.
731
	if ($disable_security)
732
		return true;
733
734
	// Are we trying to login?
735
	if (isset($_POST['contbutt']) && (!empty($_POST['user'])))
736
	{
737
		// If we've disabled security pick a suitable name!
738
		if (empty($_POST['user']))
739
			$_POST['user'] = 'Administrator';
740
741
		// Before 2.0 these column names were different!
742
		$oldDB = false;
743
		if (empty($db_type) || $db_type == 'mysql')
744
		{
745
			$request = $smcFunc['db_query']('', '
746
				SHOW COLUMNS
747
				FROM {db_prefix}members
748
				LIKE {string:member_name}',
749
				array(
750
					'member_name' => 'memberName',
751
					'db_error_skip' => true,
752
				)
753
			);
754
			if ($smcFunc['db_num_rows']($request) != 0)
755
				$oldDB = true;
756
			$smcFunc['db_free_result']($request);
757
		}
758
759
		// Get what we believe to be their details.
760
		if (!$disable_security)
761
		{
762
			if ($oldDB)
763
				$request = $smcFunc['db_query']('', '
764
					SELECT id_member, memberName AS member_name, passwd, id_group,
765
					additionalGroups AS additional_groups, lngfile
766
					FROM {db_prefix}members
767
					WHERE memberName = {string:member_name}',
768
					array(
769
						'member_name' => $_POST['user'],
770
						'db_error_skip' => true,
771
					)
772
				);
773
			else
774
				$request = $smcFunc['db_query']('', '
775
					SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile
776
					FROM {db_prefix}members
777
					WHERE member_name = {string:member_name}',
778
					array(
779
						'member_name' => $_POST['user'],
780
						'db_error_skip' => true,
781
					)
782
				);
783
			if ($smcFunc['db_num_rows']($request) != 0)
784
			{
785
				list ($id_member, $name, $password, $id_group, $addGroups, $user_language) = $smcFunc['db_fetch_row']($request);
786
787
				$groups = explode(',', $addGroups);
788
				$groups[] = $id_group;
789
790
				foreach ($groups as $k => $v)
791
					$groups[$k] = (int) $v;
792
793
				$sha_passwd = sha1(strtolower($name) . un_htmlspecialchars($_REQUEST['passwrd']));
794
			}
795
			else
796
				$upcontext['username_incorrect'] = true;
797
			$smcFunc['db_free_result']($request);
798
		}
799
		$upcontext['username'] = $_POST['user'];
800
801
		// Track whether javascript works!
802
		if (!empty($_POST['js_works']))
803
		{
804
			$upcontext['upgrade_status']['js'] = 1;
805
			$support_js = 1;
806
		}
807
		else
808
			$support_js = 0;
809
810
		// Note down the version we are coming from.
811
		if (!empty($modSettings['smfVersion']) && empty($upcontext['user']['version']))
812
			$upcontext['user']['version'] = $modSettings['smfVersion'];
813
814
		// Didn't get anywhere?
815
		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']))
816
		{
817
			// MD5?
818
			$md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user']));
819
			if ($md5pass != $password)
820
			{
821
				$upcontext['password_failed'] = true;
822
				// Disable the hashing this time.
823
				$upcontext['disable_login_hashing'] = true;
824
			}
825
		}
826
827
		if ((empty($upcontext['password_failed']) && !empty($name)) || $disable_security)
828
		{
829
			// Set the password.
830
			if (!$disable_security)
831
			{
832
				// Do we actually have permission?
833
				if (!in_array(1, $groups))
834
				{
835
					$request = $smcFunc['db_query']('', '
836
						SELECT permission
837
						FROM {db_prefix}permissions
838
						WHERE id_group IN ({array_int:groups})
839
							AND permission = {string:admin_forum}',
840
						array(
841
							'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...
842
							'admin_forum' => 'admin_forum',
843
							'db_error_skip' => true,
844
						)
845
					);
846
					if ($smcFunc['db_num_rows']($request) == 0)
847
						return throw_error('You need to be an admin to perform an upgrade!');
848
					$smcFunc['db_free_result']($request);
849
				}
850
851
				$upcontext['user']['id'] = $id_member;
852
				$upcontext['user']['name'] = $name;
853
			}
854
			else
855
			{
856
				$upcontext['user']['id'] = 1;
857
				$upcontext['user']['name'] = 'Administrator';
858
			}
859
			$upcontext['user']['pass'] = mt_rand(0, 60000);
860
			// This basically is used to match the GET variables to Settings.php.
861
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
862
863
			// Set the language to that of the user?
864
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
865
			{
866
				$user_language = basename($user_language, '.lng');
867
				$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
868
				preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
869
870
				if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
871
					$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'] . '.';
872
				elseif (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . basename($user_language, '.lng') . '.php'))
873
					$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'] . '.';
874
				else
875
				{
876
					// Set this as the new language.
877
					$upcontext['language'] = $user_language;
878
					$upcontext['upgrade_status']['lang'] = $upcontext['language'];
879
880
					// Include the file.
881
					require_once($modSettings['theme_dir'] . '/languages/Install.' . $user_language . '.php');
882
				}
883
			}
884
885
			// If we're resuming set the step and substep to be correct.
886
			if (isset($_POST['cont']))
887
			{
888
				$upcontext['current_step'] = $upcontext['user']['step'];
889
				$_GET['substep'] = $upcontext['user']['substep'];
890
			}
891
892
			return true;
893
		}
894
	}
895
896
	return false;
897
}
898
899
// Step 1: Do the maintenance and backup.
900
function UpgradeOptions()
901
{
902
	global $db_prefix, $command_line, $modSettings, $is_debug, $smcFunc, $packagesdir, $tasksdir;
903
	global $boarddir, $boardurl, $sourcedir, $maintenance, $cachedir, $upcontext, $db_type, $db_server, $db_last_error;
904
905
	$upcontext['sub_template'] = 'upgrade_options';
906
	$upcontext['page_title'] = 'Upgrade Options';
907
908
	db_extend('packages');
909
	$upcontext['karma_installed'] = array('good' => false, 'bad' => false);
910
	$member_columns = $smcFunc['db_list_columns']('{db_prefix}members');
911
912
	$upcontext['karma_installed']['good'] = in_array('karma_good', $member_columns);
913
	$upcontext['karma_installed']['bad'] = in_array('karma_bad', $member_columns);
914
915
	unset($member_columns);
916
917
	// If we've not submitted then we're done.
918
	if (empty($_POST['upcont']))
919
		return false;
920
921
	// Firstly, if they're enabling SM stat collection just do it.
922
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']))
923
	{
924
		// Attempt to register the site etc.
925
		$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
926
		if ($fp)
927
		{
928
			$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
929
			$out .= 'Host: www.simplemachines.org' . "\r\n";
930
			$out .= 'Connection: Close' . "\r\n\r\n";
931
			fwrite($fp, $out);
932
933
			$return_data = '';
934
			while (!feof($fp))
935
				$return_data .= fgets($fp, 128);
936
937
			fclose($fp);
938
939
			// Get the unique site ID.
940
			preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);
941
942 View Code Duplication
			if (!empty($ID[1]))
943
				$smcFunc['db_insert']('replace',
944
					$db_prefix . 'settings',
945
					array('variable' => 'string', 'value' => 'string'),
946
					array('allow_sm_stats', $ID[1]),
947
					array('variable')
948
				);
949
		}
950
	}
951
	else
952
		$smcFunc['db_query']('', '
953
			DELETE FROM {db_prefix}settings
954
			WHERE variable = {string:allow_sm_stats}',
955
			array(
956
				'allow_sm_stats' => 'allow_sm_stats',
957
				'db_error_skip' => true,
958
			)
959
		);
960
961
	// Deleting old karma stuff?
962
	if (!empty($_POST['delete_karma']))
963
	{
964
		// Delete old settings vars.
965
		$smcFunc['db_query']('', '
966
			DELETE FROM {db_prefix}settings
967
			WHERE variable IN ({array_string:karma_vars})',
968
			array(
969
				'karma_vars' => array('karmaMode', 'karmaTimeRestrictAdmins', 'karmaWaitTime', 'karmaMinPosts', 'karmaLabel', 'karmaSmiteLabel', 'karmaApplaudLabel'),
970
			)
971
		);
972
973
		// Cleaning up old karma member settings.
974
		if ($upcontext['karma_installed']['good'])
975
			$smcFunc['db_query']('', '
976
				ALTER TABLE {db_prefix}members
977
				DROP karma_good',
978
				array()
979
			);
980
981
		// Does karma bad was enable?
982
		if ($upcontext['karma_installed']['bad'])
983
			$smcFunc['db_query']('', '
984
				ALTER TABLE {db_prefix}members
985
				DROP karma_bad',
986
				array()
987
			);
988
989
		// Cleaning up old karma permissions.
990
		$smcFunc['db_query']('', '
991
			DELETE FROM {db_prefix}permissions
992
			WHERE permission = {string:karma_vars}',
993
			array(
994
				'karma_vars' => 'karma_edit',
995
			)
996
		);
997
	}
998
999
	// Emptying the error log?
1000
	if (!empty($_POST['empty_error']))
1001
		$smcFunc['db_query']('truncate_table', '
1002
			TRUNCATE {db_prefix}log_errors',
1003
			array(
1004
			)
1005
		);
1006
1007
	$changes = array();
1008
1009
	// Add proxy settings.
1010
	if (!isset($GLOBALS['image_proxy_maxsize']))
1011
		$changes += array(
1012
			'image_proxy_secret' => '\'' . substr(sha1(mt_rand()), 0, 20) . '\'',
1013
			'image_proxy_maxsize' => 5190,
1014
			'image_proxy_enabled' => 0,
1015
		);
1016
1017
	// If we're overriding the language follow it through.
1018
	if (isset($_GET['lang']) && file_exists($modSettings['theme_dir'] . '/languages/index.' . $_GET['lang'] . '.php'))
1019
		$changes['language'] = '\'' . $_GET['lang'] . '\'';
1020
1021
	if (!empty($_POST['maint']))
1022
	{
1023
		$changes['maintenance'] = '2';
1024
		// Remember what it was...
1025
		$upcontext['user']['main'] = $maintenance;
1026
1027
		if (!empty($_POST['maintitle']))
1028
		{
1029
			$changes['mtitle'] = '\'' . addslashes($_POST['maintitle']) . '\'';
1030
			$changes['mmessage'] = '\'' . addslashes($_POST['mainmessage']) . '\'';
1031
		}
1032
		else
1033
		{
1034
			$changes['mtitle'] = '\'Upgrading the forum...\'';
1035
			$changes['mmessage'] = '\'Don\\\'t worry, we will be back shortly with an updated forum.  It will only be a minute ;).\'';
1036
		}
1037
	}
1038
1039
	if ($command_line)
1040
		echo ' * Updating Settings.php...';
1041
1042
	// Fix some old paths.
1043
	if (substr($boarddir, 0, 1) == '.')
1044
		$changes['boarddir'] = '\'' . fixRelativePath($boarddir) . '\'';
1045
1046
	if (substr($sourcedir, 0, 1) == '.')
1047
		$changes['sourcedir'] = '\'' . fixRelativePath($sourcedir) . '\'';
1048
1049
	if (empty($cachedir) || substr($cachedir, 0, 1) == '.')
1050
		$changes['cachedir'] = '\'' . fixRelativePath($boarddir) . '/cache\'';
1051
1052
	// Not had the database type added before?
1053
	if (empty($db_type))
1054
		$changes['db_type'] = 'mysql';
1055
1056
	// If they have a "host:port" setup for the host, split that into separate values
1057
	// You should never have a : in the hostname if you're not on MySQL, but better safe than sorry
1058
	if (strpos($db_server, ':') !== false && $db_type == 'mysql')
1059
	{
1060
		list ($db_server, $db_port) = explode(':', $db_server);
1061
1062
		$changes['db_server'] = '\'' . $db_server . '\'';
1063
1064
		// Only set this if we're not using the default port
1065
		if ($db_port != ini_get('mysqli.default_port'))
1066
			$changes['db_port'] = (int) $db_port;
1067
	}
1068
	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...
1069
	{
1070
		// If db_port is set and is the same as the default, set it to ''
1071
		if ($db_type == 'mysql')
1072
		{
1073
			if ($db_port == ini_get('mysqli.default_port'))
1074
				$changes['db_port'] = '\'\'';
1075
			elseif ($db_type == 'postgresql' && $db_port == 5432)
1076
				$changes['db_port'] = '\'\'';
1077
		}
1078
	}
1079
1080
	// Maybe we haven't had this option yet?
1081
	if (empty($packagesdir))
1082
		$changes['packagesdir'] = '\'' . fixRelativePath($boarddir) . '/Packages\'';
1083
1084
	// Add support for $tasksdir var.
1085
	if (empty($tasksdir))
1086
		$changes['tasksdir'] = '\'' . fixRelativePath($sourcedir) . '/tasks\'';
1087
1088
	// @todo Maybe change the cookie name if going to 1.1, too?
1089
1090
	// Update Settings.php with the new settings.
1091
	require_once($sourcedir . '/Subs-Admin.php');
1092
	updateSettingsFile($changes);
1093
1094
	if ($command_line)
1095
		echo ' Successful.' . "\n";
1096
1097
	// Are we doing debug?
1098
	if (isset($_POST['debug']))
1099
	{
1100
		$upcontext['upgrade_status']['debug'] = true;
1101
		$is_debug = true;
1102
	}
1103
1104
	// If we're not backing up then jump one.
1105
	if (empty($_POST['backup']))
1106
		$upcontext['current_step']++;
1107
1108
	// If we've got here then let's proceed to the next step!
1109
	return true;
1110
}
1111
1112
// Backup the database - why not...
1113
function BackupDatabase()
1114
{
1115
	global $upcontext, $db_prefix, $command_line, $support_js, $file_steps, $smcFunc;
1116
1117
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1118
	$upcontext['page_title'] = 'Backup Database';
1119
1120
	// Done it already - js wise?
1121
	if (!empty($_POST['backup_done']))
1122
		return true;
1123
1124
	// Some useful stuff here.
1125
	db_extend();
1126
1127
	// Might need this as well
1128
	db_extend('packages');
1129
1130
	// Get all the table names.
1131
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1132
	$db = preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? strtr($match[1], array('`' => '')) : false;
1133
	$tables = $smcFunc['db_list_tables']($db, $filter);
1134
1135
	$table_names = array();
1136
	foreach ($tables as $table)
1137
		if (substr($table, 0, 7) !== 'backup_')
1138
			$table_names[] = $table;
1139
1140
	$upcontext['table_count'] = count($table_names);
1141
	$upcontext['cur_table_num'] = $_GET['substep'];
1142
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1143
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1144
	// For non-java auto submit...
1145
	$file_steps = $upcontext['table_count'];
1146
1147
	// What ones have we already done?
1148 View Code Duplication
	foreach ($table_names as $id => $table)
1149
		if ($id < $_GET['substep'])
1150
			$upcontext['previous_tables'][] = $table;
1151
1152
	if ($command_line)
1153
		echo 'Backing Up Tables.';
1154
1155
	// If we don't support javascript we backup here.
1156
	if (!$support_js || isset($_GET['xml']))
1157
	{
1158
		// Backup each table!
1159
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1160
		{
1161
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1162
			$upcontext['cur_table_num'] = $substep + 1;
1163
1164
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1165
1166
			// Do we need to pause?
1167
			nextSubstep($substep);
1168
1169
			backupTable($table_names[$substep]);
1170
1171
			// If this is XML to keep it nice for the user do one table at a time anyway!
1172
			if (isset($_GET['xml']))
1173
				return upgradeExit();
1174
		}
1175
1176
		if ($command_line)
1177
		{
1178
			echo "\n" . ' Successful.\'' . "\n";
1179
			flush();
1180
		}
1181
		$upcontext['step_progress'] = 100;
1182
1183
		$_GET['substep'] = 0;
1184
		// Make sure we move on!
1185
		return true;
1186
	}
1187
1188
	// Either way next place to post will be database changes!
1189
	$_GET['substep'] = 0;
1190
	return false;
1191
}
1192
1193
// Backup one table...
1194
function backupTable($table)
1195
{
1196
	global $command_line, $db_prefix, $smcFunc;
1197
1198
	if ($command_line)
1199
	{
1200
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1201
		flush();
1202
	}
1203
1204
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1205
1206
	if ($command_line)
1207
		echo ' done.';
1208
}
1209
1210
// Step 2: Everything.
1211
function DatabaseChanges()
1212
{
1213
	global $db_prefix, $modSettings, $smcFunc;
1214
	global $upcontext, $support_js, $db_type;
1215
1216
	// Have we just completed this?
1217
	if (!empty($_POST['database_done']))
1218
		return true;
1219
1220
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1221
	$upcontext['page_title'] = 'Database Changes';
1222
1223
	// All possible files.
1224
	// Name, < version, insert_on_complete
1225
	$files = array(
1226
		array('upgrade_1-0.sql', '1.1', '1.1 RC0'),
1227
		array('upgrade_1-1.sql', '2.0', '2.0 a'),
1228
		array('upgrade_2-0_' . $db_type . '.sql', '2.1', '2.1 dev0'),
1229
		array('upgrade_2-1_' . $db_type . '.sql', '3.0', SMF_VERSION),
1230
	);
1231
1232
	// How many files are there in total?
1233
	if (isset($_GET['filecount']))
1234
		$upcontext['file_count'] = (int) $_GET['filecount'];
1235
	else
1236
	{
1237
		$upcontext['file_count'] = 0;
1238
		foreach ($files as $file)
1239
		{
1240
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1241
				$upcontext['file_count']++;
1242
		}
1243
	}
1244
1245
	// Do each file!
1246
	$did_not_do = count($files) - $upcontext['file_count'];
1247
	$upcontext['step_progress'] = 0;
1248
	$upcontext['cur_file_num'] = 0;
1249
	foreach ($files as $file)
1250
	{
1251
		if ($did_not_do)
1252
			$did_not_do--;
1253
		else
1254
		{
1255
			$upcontext['cur_file_num']++;
1256
			$upcontext['cur_file_name'] = $file[0];
1257
			// Do we actually need to do this still?
1258
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1259
			{
1260
				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1261 View Code Duplication
				if ($nextFile)
1262
				{
1263
					// Only update the version of this if complete.
1264
					$smcFunc['db_insert']('replace',
1265
						$db_prefix . 'settings',
1266
						array('variable' => 'string', 'value' => 'string'),
1267
						array('smfVersion', $file[2]),
1268
						array('variable')
1269
					);
1270
1271
					$modSettings['smfVersion'] = $file[2];
1272
				}
1273
1274
				// If this is XML we only do this stuff once.
1275
				if (isset($_GET['xml']))
1276
				{
1277
					// Flag to move on to the next.
1278
					$upcontext['completed_step'] = true;
1279
					// Did we complete the whole file?
1280
					if ($nextFile)
1281
						$upcontext['current_debug_item_num'] = -1;
1282
					return upgradeExit();
1283
				}
1284
				elseif ($support_js)
1285
					break;
1286
			}
1287
			// Set the progress bar to be right as if we had - even if we hadn't...
1288
			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1289
		}
1290
	}
1291
1292
	$_GET['substep'] = 0;
1293
	// So the template knows we're done.
1294
	if (!$support_js)
1295
	{
1296
		$upcontext['changes_complete'] = true;
1297
1298
		return true;
1299
	}
1300
	return false;
1301
}
1302
1303
1304
// Delete the damn thing!
1305
function DeleteUpgrade()
1306
{
1307
	global $command_line, $language, $upcontext, $boarddir, $sourcedir, $forum_version, $user_info, $maintenance, $smcFunc, $db_type;
1308
1309
	// Now it's nice to have some of the basic SMF source files.
1310
	if (!isset($_GET['ssi']) && !$command_line)
1311
		redirectLocation('&ssi=1');
1312
1313
	$upcontext['sub_template'] = 'upgrade_complete';
1314
	$upcontext['page_title'] = 'Upgrade Complete';
1315
1316
	$endl = $command_line ? "\n" : '<br>' . "\n";
1317
1318
	$changes = array(
1319
		'language' => '\'' . (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language) . '\'',
1320
		'db_error_send' => '1',
1321
		'upgradeData' => '#remove#',
1322
	);
1323
1324
	// Are we in maintenance mode?
1325
	if (isset($upcontext['user']['main']))
1326
	{
1327
		if ($command_line)
1328
			echo ' * ';
1329
		$upcontext['removed_maintenance'] = true;
1330
		$changes['maintenance'] = $upcontext['user']['main'];
1331
	}
1332
	// Otherwise if somehow we are in 2 let's go to 1.
1333
	elseif (!empty($maintenance) && $maintenance == 2)
1334
		$changes['maintenance'] = 1;
1335
1336
	// Wipe this out...
1337
	$upcontext['user'] = array();
1338
1339
	require_once($sourcedir . '/Subs-Admin.php');
1340
	updateSettingsFile($changes);
1341
1342
	// Clean any old cache files away.
1343
	upgrade_clean_cache();
1344
1345
	// Can we delete the file?
1346
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1347
1348
	// Now is the perfect time to fetch the SM files.
1349
	if ($command_line)
1350
		cli_scheduled_fetchSMfiles();
1351
	else
1352
	{
1353
		require_once($sourcedir . '/ScheduledTasks.php');
1354
		$forum_version = SMF_VERSION; // The variable is usually defined in index.php so lets just use the constant to do it for us.
1355
		scheduled_fetchSMfiles(); // Now go get those files!
1356
	}
1357
1358
	// Log what we've done.
1359
	if (empty($user_info['id']))
1360
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
1361
1362
	// Log the action manually, so CLI still works.
1363
	$smcFunc['db_insert']('',
1364
		'{db_prefix}log_actions',
1365
		array(
1366
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'inet', 'action' => 'string',
1367
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
1368
		),
1369
		array(
1370
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
1371
			0, 0, 0, json_encode(array('version' => $forum_version, 'member' => $user_info['id'])),
1372
		),
1373
		array('id_action')
1374
	);
1375
	$user_info['id'] = 0;
1376
1377
	// Save the current database version.
1378
	$server_version = $smcFunc['db_server_info']();
1379 View Code Duplication
	if ($db_type == 'mysql' && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51')))
1380
		updateSettings(array('db_mysql_group_by_fix' => '1'));
1381
1382
	if ($command_line)
1383
	{
1384
		echo $endl;
1385
		echo 'Upgrade Complete!', $endl;
1386
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
1387
		exit;
1388
	}
1389
1390
	// Make sure it says we're done.
1391
	$upcontext['overall_percent'] = 100;
1392
	if (isset($upcontext['step_progress']))
1393
		unset($upcontext['step_progress']);
1394
1395
	$_GET['substep'] = 0;
1396
	return false;
1397
}
1398
1399
// Just like the built in one, but setup for CLI to not use themes.
1400
function cli_scheduled_fetchSMfiles()
1401
{
1402
	global $sourcedir, $language, $forum_version, $modSettings, $smcFunc;
1403
1404
	if (empty($modSettings['time_format']))
1405
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1406
1407
	// What files do we want to get
1408
	$request = $smcFunc['db_query']('', '
1409
		SELECT id_file, filename, path, parameters
1410
		FROM {db_prefix}admin_info_files',
1411
		array(
1412
		)
1413
	);
1414
1415
	$js_files = array();
1416 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
1417
	{
1418
		$js_files[$row['id_file']] = array(
1419
			'filename' => $row['filename'],
1420
			'path' => $row['path'],
1421
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode($forum_version)),
1422
		);
1423
	}
1424
	$smcFunc['db_free_result']($request);
1425
1426
	// We're gonna need fetch_web_data() to pull this off.
1427
	require_once($sourcedir . '/Subs-Package.php');
1428
1429
	foreach ($js_files as $ID_FILE => $file)
1430
	{
1431
		// Create the url
1432
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'http://www.simplemachines.org' : '';
1433
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1434
1435
		// Get the file
1436
		$file_data = fetch_web_data($url);
1437
1438
		// If we got an error - give up - the site might be down.
1439
		if ($file_data === false)
1440
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
1441
1442
		// Save the file to the database.
1443
		$smcFunc['db_query']('substring', '
1444
			UPDATE {db_prefix}admin_info_files
1445
			SET data = SUBSTRING({string:file_data}, 1, 65534)
1446
			WHERE id_file = {int:id_file}',
1447
			array(
1448
				'id_file' => $ID_FILE,
1449
				'file_data' => $file_data,
1450
			)
1451
		);
1452
	}
1453
	return true;
1454
}
1455
1456
function convertSettingsToTheme()
1457
{
1458
	global $db_prefix, $modSettings, $smcFunc;
1459
1460
	$values = array(
1461
		'show_latest_member' => @$GLOBALS['showlatestmember'],
1462
		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
1463
		'show_modify' => @$GLOBALS['showmodify'],
1464
		'show_user_images' => @$GLOBALS['showuserpic'],
1465
		'show_blurb' => @$GLOBALS['showusertext'],
1466
		'show_gender' => @$GLOBALS['showgenderimage'],
1467
		'show_newsfader' => @$GLOBALS['shownewsfader'],
1468
		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
1469
		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
1470
		'linktree_link' => @$GLOBALS['curposlinks'],
1471
		'show_profile_buttons' => @$GLOBALS['profilebutton'],
1472
		'show_mark_read' => @$GLOBALS['showmarkread'],
1473
		'newsfader_time' => @$GLOBALS['fadertime'],
1474
		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
1475
		'enable_news' => @$GLOBALS['enable_news'],
1476
		'return_to_post' => @$modSettings['returnToPost'],
1477
	);
1478
1479
	$themeData = array();
1480
	foreach ($values as $variable => $value)
1481
	{
1482
		if (!isset($value) || $value === null)
1483
			$value = 0;
1484
1485
		$themeData[] = array(0, 1, $variable, $value);
1486
	}
1487 View Code Duplication
	if (!empty($themeData))
1488
	{
1489
		$smcFunc['db_insert']('ignore',
1490
			$db_prefix . 'themes',
1491
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
1492
			$themeData,
1493
			array('id_member', 'id_theme', 'variable')
1494
		);
1495
	}
1496
}
1497
1498
// This function only works with MySQL but that's fine as it is only used for v1.0.
1499
function convertSettingstoOptions()
1500
{
1501
	global $modSettings, $smcFunc;
1502
1503
	// Format: new_setting -> old_setting_name.
1504
	$values = array(
1505
		'calendar_start_day' => 'cal_startmonday',
1506
		'view_newest_first' => 'viewNewestFirst',
1507
		'view_newest_pm_first' => 'viewNewestFirst',
1508
	);
1509
1510
	foreach ($values as $variable => $value)
1511
	{
1512
		if (empty($modSettings[$value[0]]))
1513
			continue;
1514
1515
		$smcFunc['db_query']('', '
1516
			INSERT IGNORE INTO {db_prefix}themes
1517
				(id_member, id_theme, variable, value)
1518
			SELECT id_member, 1, {string:variable}, {string:value}
1519
			FROM {db_prefix}members',
1520
			array(
1521
				'variable' => $variable,
1522
				'value' => $modSettings[$value[0]],
1523
				'db_error_skip' => true,
1524
			)
1525
		);
1526
1527
		$smcFunc['db_query']('', '
1528
			INSERT IGNORE INTO {db_prefix}themes
1529
				(id_member, id_theme, variable, value)
1530
			VALUES (-1, 1, {string:variable}, {string:value})',
1531
			array(
1532
				'variable' => $variable,
1533
				'value' => $modSettings[$value[0]],
1534
				'db_error_skip' => true,
1535
			)
1536
		);
1537
	}
1538
}
1539
1540
function php_version_check()
1541
{
1542
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
1543
}
1544
1545
function db_version_check()
1546
{
1547
	global $db_type, $databases;
1548
1549
	$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...
1550
	$curver = preg_replace('~\-.+?$~', '', $curver);
1551
1552
	return version_compare($databases[$db_type]['version'], $curver, '<=');
1553
}
1554
1555
function fixRelativePath($path)
1556
{
1557
	global $install_path;
1558
1559
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
1560
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
1561
}
1562
1563
function parse_sql($filename)
1564
{
1565
	global $db_prefix, $db_collation, $boarddir, $boardurl, $command_line, $file_steps, $step_progress, $custom_warning;
1566
	global $upcontext, $support_js, $is_debug, $smcFunc, $databases, $db_type, $db_character_set;
1567
1568
/*
1569
	Failure allowed on:
1570
		- INSERT INTO but not INSERT IGNORE INTO.
1571
		- UPDATE IGNORE but not UPDATE.
1572
		- ALTER TABLE and ALTER IGNORE TABLE.
1573
		- DROP TABLE.
1574
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
1575
1576
	If a comment...
1577
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
1578
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
1579
		- is only ---#, it is "done." and then a break - only shown in debug.
1580
		- begins with ---{ it is a code block terminating at ---}.
1581
1582
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
1583
1584
	Replaces the following variables:
1585
		- {$boarddir}
1586
		- {$boardurl}
1587
		- {$db_prefix}
1588
		- {$db_collation}
1589
*/
1590
1591
	// May want to use extended functionality.
1592
	db_extend();
1593
	db_extend('packages');
1594
1595
	// Our custom error handler - does nothing but does stop public errors from XML!
1596
	set_error_handler(
1597
		function ($errno, $errstr, $errfile, $errline) use ($support_js)
1598
		{
1599
			if ($support_js)
1600
				return true;
1601
			else
1602
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
1603
		}
1604
	);
1605
1606
	// If we're on MySQL supporting collations then let's find out what the members table uses and put it in a global var - to allow upgrade script to match collations!
1607
	if (!empty($databases[$db_type]['utf8_support']) && version_compare($databases[$db_type]['utf8_version'], eval($databases[$db_type]['utf8_version_check']), '>'))
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...
1608
	{
1609
		$request = $smcFunc['db_query']('', '
1610
			SHOW TABLE STATUS
1611
			LIKE {string:table_name}',
1612
			array(
1613
				'table_name' => "{$db_prefix}members",
1614
				'db_error_skip' => true,
1615
			)
1616
		);
1617
		if ($smcFunc['db_num_rows']($request) === 0)
1618
			die('Unable to find members table!');
1619
		$table_status = $smcFunc['db_fetch_assoc']($request);
1620
		$smcFunc['db_free_result']($request);
1621
1622
		if (!empty($table_status['Collation']))
1623
		{
1624
			$request = $smcFunc['db_query']('', '
1625
				SHOW COLLATION
1626
				LIKE {string:collation}',
1627
				array(
1628
					'collation' => $table_status['Collation'],
1629
					'db_error_skip' => true,
1630
				)
1631
			);
1632
			// Got something?
1633
			if ($smcFunc['db_num_rows']($request) !== 0)
1634
				$collation_info = $smcFunc['db_fetch_assoc']($request);
1635
			$smcFunc['db_free_result']($request);
1636
1637
			// Excellent!
1638
			if (!empty($collation_info['Collation']) && !empty($collation_info['Charset']))
1639
				$db_collation = ' CHARACTER SET ' . $collation_info['Charset'] . ' COLLATE ' . $collation_info['Collation'];
1640
		}
1641
	}
1642
	if (empty($db_collation))
1643
		$db_collation = '';
1644
1645
	$endl = $command_line ? "\n" : '<br>' . "\n";
1646
1647
	$lines = file($filename);
1648
1649
	$current_type = 'sql';
1650
	$current_data = '';
1651
	$substep = 0;
1652
	$last_step = '';
1653
1654
	// Make sure all newly created tables will have the proper characters set.
1655
	if (isset($db_character_set) && $db_character_set === 'utf8')
1656
		$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
1657
1658
	// Count the total number of steps within this file - for progress.
1659
	$file_steps = substr_count(implode('', $lines), '---#');
1660
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
1661
	$upcontext['debug_items'] = $file_steps;
1662
	$upcontext['current_item_num'] = 0;
1663
	$upcontext['current_item_name'] = '';
1664
	$upcontext['current_debug_item_num'] = 0;
1665
	$upcontext['current_debug_item_name'] = '';
1666
	// This array keeps a record of what we've done in case java is dead...
1667
	$upcontext['actioned_items'] = array();
1668
1669
	$done_something = false;
1670
1671
	foreach ($lines as $line_number => $line)
1672
	{
1673
		$do_current = $substep >= $_GET['substep'];
1674
1675
		// Get rid of any comments in the beginning of the line...
1676
		if (substr(trim($line), 0, 2) === '/*')
1677
			$line = preg_replace('~/\*.+?\*/~', '', $line);
1678
1679
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
1680
		if ($is_debug && !$support_js && $command_line)
1681
			flush();
1682
1683
		if (trim($line) === '')
1684
			continue;
1685
1686
		if (trim(substr($line, 0, 3)) === '---')
1687
		{
1688
			$type = substr($line, 3, 1);
1689
1690
			// An error??
1691
			if (trim($current_data) != '' && $type !== '}')
1692
			{
1693
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
1694
				if ($command_line)
1695
					echo $upcontext['error_message'];
1696
			}
1697
1698
			if ($type == ' ')
1699
			{
1700
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
1701
				{
1702
					echo ' Successful.', $endl;
1703
					flush();
1704
				}
1705
1706
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
1707
				$upcontext['current_item_num']++;
1708
				$upcontext['current_item_name'] = $last_step;
1709
1710
				if ($do_current)
1711
				{
1712
					$upcontext['actioned_items'][] = $last_step;
1713
					if ($command_line)
1714
						echo ' * ';
1715
				}
1716
			}
1717
			elseif ($type == '#')
1718
			{
1719
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
1720
1721
				$upcontext['current_debug_item_num']++;
1722
				if (trim($line) != '---#')
1723
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
1724
1725
				// Have we already done something?
1726
				if (isset($_GET['xml']) && $done_something)
1727
				{
1728
					restore_error_handler();
1729
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
1730
				}
1731
1732
				if ($do_current)
1733
				{
1734
					if (trim($line) == '---#' && $command_line)
1735
						echo ' done.', $endl;
1736
					elseif ($command_line)
1737
						echo ' +++ ', rtrim(substr($line, 4));
1738
					elseif (trim($line) != '---#')
1739
					{
1740
						if ($is_debug)
1741
							$upcontext['actioned_items'][] = htmlspecialchars(rtrim(substr($line, 4)));
1742
					}
1743
				}
1744
1745
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
1746
				{
1747
					if ($command_line)
1748
						echo ' * ';
1749
					else
1750
						$upcontext['actioned_items'][] = $last_step;
1751
				}
1752
1753
				// Small step - only if we're actually doing stuff.
1754
				if ($do_current)
1755
					nextSubstep(++$substep);
1756
				else
1757
					$substep++;
1758
			}
1759
			elseif ($type == '{')
1760
				$current_type = 'code';
1761
			elseif ($type == '}')
1762
			{
1763
				$current_type = 'sql';
1764
1765
				if (!$do_current)
1766
				{
1767
					$current_data = '';
1768
					continue;
1769
				}
1770
1771
				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...
1772
				{
1773
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
1774
					if ($command_line)
1775
						echo $upcontext['error_message'];
1776
				}
1777
1778
				// Done with code!
1779
				$current_data = '';
1780
				$done_something = true;
1781
			}
1782
1783
			continue;
1784
		}
1785
1786
		$current_data .= $line;
1787
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
1788
		{
1789
			if ((!$support_js || isset($_GET['xml'])))
1790
			{
1791
				if (!$do_current)
1792
				{
1793
					$current_data = '';
1794
					continue;
1795
				}
1796
1797
				$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));
1798
1799
				upgrade_query($current_data);
1800
1801
				// @todo This will be how it kinda does it once mysql all stripped out - needed for postgre (etc).
1802
				/*
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...
1803
				$result = $smcFunc['db_query']('', $current_data, false, false);
1804
				// Went wrong?
1805
				if (!$result)
1806
				{
1807
					// Bit of a bodge - do we want the error?
1808
					if (!empty($upcontext['return_error']))
1809
					{
1810
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
1811
						return false;
1812
					}
1813
				}*/
1814
				$done_something = true;
1815
			}
1816
			$current_data = '';
1817
		}
1818
		// If this is xml based and we're just getting the item name then that's grand.
1819
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
1820
		{
1821
			restore_error_handler();
1822
			return false;
1823
		}
1824
1825
		// Clean up by cleaning any step info.
1826
		$step_progress = array();
1827
		$custom_warning = '';
1828
	}
1829
1830
	// Put back the error handler.
1831
	restore_error_handler();
1832
1833
	if ($command_line)
1834
	{
1835
		echo ' Successful.' . "\n";
1836
		flush();
1837
	}
1838
1839
	$_GET['substep'] = 0;
1840
	return true;
1841
}
1842
1843
function upgrade_query($string, $unbuffered = false)
1844
{
1845
	global $db_connection, $db_server, $db_user, $db_passwd, $db_type, $command_line, $upcontext, $upgradeurl, $modSettings;
1846
	global $db_name, $db_unbuffered, $smcFunc;
1847
1848
	// Get the query result - working around some SMF specific security - just this once!
1849
	$modSettings['disableQueryCheck'] = true;
1850
	$db_unbuffered = $unbuffered;
1851
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
1852
	$db_unbuffered = false;
1853
1854
	// Failure?!
1855
	if ($result !== false)
1856
		return $result;
1857
1858
	$db_error_message = $smcFunc['db_error']($db_connection);
1859
	// If MySQL we do something more clever.
1860
	if ($db_type == 'mysql')
1861
	{
1862
		$mysqli_errno = mysqli_errno($db_connection);
1863
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR'));
1864
1865
		// Error numbers:
1866
		//    1016: Can't open file '....MYI'
1867
		//    1050: Table already exists.
1868
		//    1054: Unknown column name.
1869
		//    1060: Duplicate column name.
1870
		//    1061: Duplicate key name.
1871
		//    1062: Duplicate entry for unique key.
1872
		//    1068: Multiple primary keys.
1873
		//    1072: Key column '%s' doesn't exist in table.
1874
		//    1091: Can't drop key, doesn't exist.
1875
		//    1146: Table doesn't exist.
1876
		//    2013: Lost connection to server during query.
1877
1878
		if ($mysqli_errno == 1016)
1879
		{
1880
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
1881
			{
1882
				mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
1883
				$result = mysqli_query($db_connection, $string);
1884
				if ($result !== false)
1885
					return $result;
1886
			}
1887
		}
1888
		elseif ($mysqli_errno == 2013)
1889
		{
1890
			$db_connection = mysqli_connect($db_server, $db_user, $db_passwd);
1891
			mysqli_select_db($db_connection, $db_name);
1892
			if ($db_connection)
1893
			{
1894
				$result = mysqli_query($db_connection, $string);
1895
				if ($result !== false)
1896
					return $result;
1897
			}
1898
		}
1899
		// Duplicate column name... should be okay ;).
1900 View Code Duplication
		elseif (in_array($mysqli_errno, array(1060, 1061, 1068, 1091)))
1901
			return false;
1902
		// Duplicate insert... make sure it's the proper type of query ;).
1903 View Code Duplication
		elseif (in_array($mysqli_errno, array(1054, 1062, 1146)) && $error_query)
1904
			return false;
1905
		// Creating an index on a non-existent column.
1906
		elseif ($mysqli_errno == 1072)
1907
			return false;
1908
		elseif ($mysqli_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
1909
			return false;
1910
	}
1911
	// If a table already exists don't go potty.
1912
	else
1913
	{
1914
		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U')))
1915
		{
1916
			if (strpos($db_error_message, 'exist') !== false)
1917
				return true;
1918
		}
1919
		elseif (strpos(trim($string), 'INSERT ') !== false)
1920
		{
1921
			if (strpos($db_error_message, 'duplicate') !== false)
1922
				return true;
1923
		}
1924
	}
1925
1926
	// Get the query string so we pass everything.
1927
	$query_string = '';
1928
	foreach ($_GET as $k => $v)
1929
		$query_string .= ';' . $k . '=' . $v;
1930
	if (strlen($query_string) != 0)
1931
		$query_string = '?' . substr($query_string, 1);
1932
1933
	if ($command_line)
1934
	{
1935
		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
1936
		die;
1937
	}
1938
1939
	// Bit of a bodge - do we want the error?
1940
	if (!empty($upcontext['return_error']))
1941
	{
1942
		$upcontext['error_message'] = $db_error_message;
1943
		$upcontext['error_string'] = $string;
1944
		return false;
1945
	}
1946
1947
	// Otherwise we have to display this somewhere appropriate if possible.
1948
	$upcontext['forced_error_message'] = '
1949
			<strong>Unsuccessful!</strong><br>
1950
1951
			<div style="margin: 2ex;">
1952
				This query:
1953
				<blockquote><pre>' . nl2br(htmlspecialchars(trim($string))) . ';</pre></blockquote>
1954
1955
				Caused the error:
1956
				<blockquote>' . nl2br(htmlspecialchars($db_error_message)) . '</blockquote>
1957
			</div>
1958
1959
			<form action="' . $upgradeurl . $query_string . '" method="post">
1960
				<input type="submit" value="Try again" class="button_submit">
1961
			</form>
1962
		</div>';
1963
1964
	upgradeExit();
1965
}
1966
1967
// This performs a table alter, but does it unbuffered so the script can time out professionally.
1968
function protected_alter($change, $substep, $is_test = false)
1969
{
1970
	global $db_prefix, $smcFunc;
1971
1972
	db_extend('packages');
1973
1974
	// Firstly, check whether the current index/column exists.
1975
	$found = false;
1976
	if ($change['type'] === 'column')
1977
	{
1978
		$columns = $smcFunc['db_list_columns']('{db_prefix}' . $change['table'], true);
1979
		foreach ($columns as $column)
1980
		{
1981
			// Found it?
1982
			if ($column['name'] === $change['name'])
1983
			{
1984
				$found |= 1;
1985
				// Do some checks on the data if we have it set.
1986
				if (isset($change['col_type']))
1987
					$found &= $change['col_type'] === $column['type'];
1988
				if (isset($change['null_allowed']))
1989
					$found &= $column['null'] == $change['null_allowed'];
1990
				if (isset($change['default']))
1991
					$found &= $change['default'] === $column['default'];
1992
			}
1993
		}
1994
	}
1995
	elseif ($change['type'] === 'index')
1996
	{
1997
		$request = upgrade_query('
1998
			SHOW INDEX
1999
			FROM ' . $db_prefix . $change['table']);
2000
		if ($request !== false)
2001
		{
2002
			$cur_index = array();
2003
2004
			while ($row = $smcFunc['db_fetch_assoc']($request))
2005
				if ($row['Key_name'] === $change['name'])
2006
					$cur_index[(int) $row['Seq_in_index']] = $row['Column_name'];
2007
2008
			ksort($cur_index, SORT_NUMERIC);
2009
			$found = array_values($cur_index) === $change['target_columns'];
2010
2011
			$smcFunc['db_free_result']($request);
2012
		}
2013
	}
2014
2015
	// If we're trying to add and it's added, we're done.
2016
	if ($found && in_array($change['method'], array('add', 'change')))
2017
		return true;
2018
	// Otherwise if we're removing and it wasn't found we're also done.
2019
	elseif (!$found && in_array($change['method'], array('remove', 'change_remove')))
2020
		return true;
2021
	// Otherwise is it just a test?
2022
	elseif ($is_test)
2023
		return false;
2024
2025
	// Not found it yet? Bummer! How about we see if we're currently doing it?
2026
	$running = false;
2027
	$found = false;
2028
	while (1 == 1)
2029
	{
2030
		$request = upgrade_query('
2031
			SHOW FULL PROCESSLIST');
2032
		while ($row = $smcFunc['db_fetch_assoc']($request))
2033
		{
2034
			if (strpos($row['Info'], 'ALTER TABLE ' . $db_prefix . $change['table']) !== false && strpos($row['Info'], $change['text']) !== false)
2035
				$found = true;
2036
		}
2037
2038
		// Can't find it? Then we need to run it fools!
2039
		if (!$found && !$running)
2040
		{
2041
			$smcFunc['db_free_result']($request);
2042
2043
			$success = upgrade_query('
2044
				ALTER TABLE ' . $db_prefix . $change['table'] . '
2045
				' . $change['text'], true) !== false;
2046
2047
			if (!$success)
2048
				return false;
2049
2050
			// Return
2051
			$running = true;
2052
		}
2053
		// What if we've not found it, but we'd ran it already? Must of completed.
2054
		elseif (!$found)
2055
		{
2056
			$smcFunc['db_free_result']($request);
2057
			return true;
2058
		}
2059
2060
		// Pause execution for a sec or three.
2061
		sleep(3);
2062
2063
		// Can never be too well protected.
2064
		nextSubstep($substep);
2065
	}
2066
2067
	// Protect it.
2068
	nextSubstep($substep);
2069
}
2070
2071
/**
2072
 * Alter a text column definition preserving its character set.
2073
 *
2074
 * @param array $change
2075
 * @param int $substep
2076
 */
2077
function textfield_alter($change, $substep)
2078
{
2079
	global $db_prefix, $smcFunc;
2080
2081
	$request = $smcFunc['db_query']('', '
2082
		SHOW FULL COLUMNS
2083
		FROM {db_prefix}' . $change['table'] . '
2084
		LIKE {string:column}',
2085
		array(
2086
			'column' => $change['column'],
2087
			'db_error_skip' => true,
2088
		)
2089
	);
2090
	if ($smcFunc['db_num_rows']($request) === 0)
2091
		die('Unable to find column ' . $change['column'] . ' inside table ' . $db_prefix . $change['table']);
2092
	$table_row = $smcFunc['db_fetch_assoc']($request);
2093
	$smcFunc['db_free_result']($request);
2094
2095
	// If something of the current column definition is different, fix it.
2096
	$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']);
2097
2098
	// Columns that previously allowed null, need to be converted first.
2099
	$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
2100
2101
	// Get the character set that goes with the collation of the column.
2102 View Code Duplication
	if ($column_fix && !empty($table_row['Collation']))
2103
	{
2104
		$request = $smcFunc['db_query']('', '
2105
			SHOW COLLATION
2106
			LIKE {string:collation}',
2107
			array(
2108
				'collation' => $table_row['Collation'],
2109
				'db_error_skip' => true,
2110
			)
2111
		);
2112
		// No results? Just forget it all together.
2113
		if ($smcFunc['db_num_rows']($request) === 0)
2114
			unset($table_row['Collation']);
2115
		else
2116
			$collation_info = $smcFunc['db_fetch_assoc']($request);
2117
		$smcFunc['db_free_result']($request);
2118
	}
2119
2120
	if ($column_fix)
2121
	{
2122
		// Make sure there are no NULL's left.
2123
		if ($null_fix)
2124
			$smcFunc['db_query']('', '
2125
				UPDATE {db_prefix}' . $change['table'] . '
2126
				SET ' . $change['column'] . ' = {string:default}
2127
				WHERE ' . $change['column'] . ' IS NULL',
2128
				array(
2129
					'default' => isset($change['default']) ? $change['default'] : '',
2130
					'db_error_skip' => true,
2131
				)
2132
			);
2133
2134
		// Do the actual alteration.
2135
		$smcFunc['db_query']('', '
2136
			ALTER TABLE {db_prefix}' . $change['table'] . '
2137
			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}' : ''),
2138
			array(
2139
				'default' => isset($change['default']) ? $change['default'] : '',
2140
				'db_error_skip' => true,
2141
			)
2142
		);
2143
	}
2144
	nextSubstep($substep);
2145
}
2146
2147
// Check if we need to alter this query.
2148
function checkChange(&$change)
2149
{
2150
	global $smcFunc, $db_type, $databases;
2151
	static $database_version, $where_field_support;
2152
2153
	// Attempt to find a database_version.
2154
	if (empty($database_version))
2155
	{
2156
		$database_version = $databases[$db_type]['version_check'];
2157
		$where_field_support = $db_type == 'mysql' && version_compare('5.0', $database_version, '<=');
2158
	}
2159
2160
	// Not a column we need to check on?
2161
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
2162
		return;
2163
2164
	// Break it up you (six|seven).
2165
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
2166
2167
	// Can we support a shortcut method?
2168
	if ($where_field_support)
2169
	{
2170
		// Get the details about this change.
2171
		$request = $smcFunc['db_query']('', '
2172
			SHOW FIELDS
2173
			FROM {db_prefix}{raw:table}
2174
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
2175
			array(
2176
				'table' => $change['table'],
2177
				'old_name' => $temp[1],
2178
				'new_name' => $temp[2],
2179
		));
2180
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2181
		if ($smcFunc['db_num_rows'] != 1)
2182
			return;
2183
2184
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
2185
		$smcFunc['db_free_result']($request);
2186
	}
2187
	else
2188
	{
2189
		// Do this the old fashion, sure method way.
2190
		$request = $smcFunc['db_query']('', '
2191
			SHOW FIELDS
2192
			FROM {db_prefix}{raw:table}',
2193
			array(
2194
				'table' => $change['table'],
2195
		));
2196
		// Mayday!
2197
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2198
		if ($smcFunc['db_num_rows'] == 0)
2199
			return;
2200
2201
		// Oh where, oh where has my little field gone. Oh where can it be...
2202
		while ($row = $smcFunc['db_query']($request))
2203
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
2204
			{
2205
				$current_type = $row['Type'];
2206
				break;
2207
			}
2208
	}
2209
2210
	// If this doesn't match, the column may of been altered for a reason.
2211
	if (trim($current_type) != trim($temp[3]))
2212
		$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...
2213
2214
	// Piece this back together.
2215
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
2216
}
2217
2218
// The next substep.
2219
function nextSubstep($substep)
2220
{
2221
	global $start_time, $timeLimitThreshold, $command_line, $custom_warning;
2222
	global $step_progress, $is_debug, $upcontext;
2223
2224
	if ($_GET['substep'] < $substep)
2225
		$_GET['substep'] = $substep;
2226
2227
	if ($command_line)
2228
	{
2229
		if (time() - $start_time > 1 && empty($is_debug))
2230
		{
2231
			echo '.';
2232
			$start_time = time();
2233
		}
2234
		return;
2235
	}
2236
2237
	@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...
2238
	if (function_exists('apache_reset_timeout'))
2239
		@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...
2240
2241
	if (time() - $start_time <= $timeLimitThreshold)
2242
		return;
2243
2244
	// Do we have some custom step progress stuff?
2245
	if (!empty($step_progress))
2246
	{
2247
		$upcontext['substep_progress'] = 0;
2248
		$upcontext['substep_progress_name'] = $step_progress['name'];
2249
		if ($step_progress['current'] > $step_progress['total'])
2250
			$upcontext['substep_progress'] = 99.9;
2251
		else
2252
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2253
2254
		// Make it nicely rounded.
2255
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2256
	}
2257
2258
	// If this is XML we just exit right away!
2259
	if (isset($_GET['xml']))
2260
		return upgradeExit();
2261
2262
	// We're going to pause after this!
2263
	$upcontext['pause'] = true;
2264
2265
	$upcontext['query_string'] = '';
2266
	foreach ($_GET as $k => $v)
2267
	{
2268
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2269
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2270
	}
2271
2272
	// Custom warning?
2273
	if (!empty($custom_warning))
2274
		$upcontext['custom_warning'] = $custom_warning;
2275
2276
	upgradeExit();
2277
}
2278
2279
function cmdStep0()
2280
{
2281
	global $boarddir, $sourcedir, $modSettings, $start_time, $cachedir, $databases, $db_type, $smcFunc, $upcontext;
2282
	global $is_debug;
2283
	$start_time = time();
2284
2285
	ob_end_clean();
2286
	ob_implicit_flush(true);
2287
	@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...
2288
2289
	if (!isset($_SERVER['argv']))
2290
		$_SERVER['argv'] = array();
2291
	$_GET['maint'] = 1;
2292
2293
	foreach ($_SERVER['argv'] as $i => $arg)
2294
	{
2295
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2296
			$_GET['lang'] = $match[1];
2297
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2298
			continue;
2299
		elseif ($arg == '--no-maintenance')
2300
			$_GET['maint'] = 0;
2301
		elseif ($arg == '--debug')
2302
			$is_debug = true;
2303
		elseif ($arg == '--backup')
2304
			$_POST['backup'] = 1;
2305
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2306
			$_GET['conv'] = 1;
2307
		elseif ($i != 0)
2308
		{
2309
			echo 'SMF Command-line Upgrader
2310
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2311
2312
    --language=LANG         Reset the forum\'s language to LANG.
2313
    --no-maintenance        Don\'t put the forum into maintenance mode.
2314
    --debug                 Output debugging information.
2315
    --backup                Create backups of tables with "backup_" prefix.';
2316
			echo "\n";
2317
			exit;
2318
		}
2319
	}
2320
2321
	if (!php_version_check())
2322
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
2323
	if (!db_version_check())
2324
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2325
2326
	// Do some checks to make sure they have proper privileges
2327
	db_extend('packages');
2328
2329
	// CREATE
2330
	$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');
2331
2332
	// ALTER
2333
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'tinytext', 'null' => false, 'default' => ''));
2334
2335
	// DROP
2336
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2337
2338
	// Sorry... we need CREATE, ALTER and DROP
2339 View Code Duplication
	if (!$create || !$alter || !$drop)
2340
		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);
2341
2342
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2343
		&& @file_exists($sourcedir . '/QueryString.php')
2344
		&& @file_exists($sourcedir . '/ManageBoards.php');
2345
	if (!$check && !isset($modSettings['smfVersion']))
2346
		print_error('Error: Some files are missing or out-of-date.', true);
2347
2348
	// Do a quick version spot check.
2349
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
2350
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2351
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2352
		print_error('Error: Some files have not yet been updated properly.');
2353
2354
	// Make sure Settings.php is writable.
2355
		quickFileWritable($boarddir . '/Settings.php');
2356
	if (!is_writable($boarddir . '/Settings.php'))
2357
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2358
2359
	// Make sure Settings_bak.php is writable.
2360
		quickFileWritable($boarddir . '/Settings_bak.php');
2361
	if (!is_writable($boarddir . '/Settings_bak.php'))
2362
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2363
2364 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
2365
		print_error('Error: Unable to obtain write access to "agreement.txt".');
2366
	elseif (isset($modSettings['agreement']))
2367
	{
2368
		$fp = fopen($boarddir . '/agreement.txt', 'w');
2369
		fwrite($fp, $modSettings['agreement']);
2370
		fclose($fp);
2371
	}
2372
2373
	// Make sure Themes is writable.
2374
	quickFileWritable($modSettings['theme_dir']);
2375
2376
	if (!is_writable($modSettings['theme_dir']) && !isset($modSettings['smfVersion']))
2377
		print_error('Error: Unable to obtain write access to "Themes".');
2378
2379
	// Make sure cache directory exists and is writable!
2380
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
2381
	if (!file_exists($cachedir_temp))
2382
		@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...
2383
2384
	// Make sure the cache temp dir is writable.
2385
	quickFileWritable($cachedir_temp);
2386
2387
	if (!is_writable($cachedir_temp))
2388
		print_error('Error: Unable to obtain write access to "cache".', true);
2389
2390
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
2391
		print_error('Error: Unable to find language files!', true);
2392
	else
2393
	{
2394
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
2395
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
2396
2397
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
2398
			print_error('Error: Language files out of date.', true);
2399
		if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
2400
			print_error('Error: Install language is missing for selected language.', true);
2401
2402
		// Otherwise include it!
2403
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
2404
	}
2405
2406
	// Make sure we skip the HTML for login.
2407
	$_POST['upcont'] = true;
2408
	$upcontext['current_step'] = 1;
2409
}
2410
2411
/**
2412
 * Handles converting your database to UTF-8
2413
 */
2414
function ConvertUtf8()
2415
{
2416
	global $upcontext, $db_character_set, $sourcedir, $smcFunc, $modSettings, $language, $db_prefix, $db_type, $command_line;
2417
2418
	// First make sure they aren't already on UTF-8 before we go anywhere...
2419
	if ($db_type == 'postgresql' || ($db_character_set === 'utf8' && !empty($modSettings['global_character_set']) && $modSettings['global_character_set'] === 'UTF-8'))
2420
	{
2421
		$smcFunc['db_insert']('replace',
2422
			'{db_prefix}settings',
2423
			array('variable' => 'string', 'value' => 'string'),
2424
			array(array('global_character_set', 'UTF-8')),
2425
			array('variable')
2426
		);
2427
2428
		return true;
2429
	}
2430
	else
2431
	{
2432
		$upcontext['page_title'] = 'Converting to UTF8';
2433
		$upcontext['sub_template'] = isset($_GET['xml']) ? 'convert_xml' : 'convert_utf8';
2434
2435
		// The character sets used in SMF's language files with their db equivalent.
2436
		$charsets = array(
2437
			// Armenian
2438
			'armscii8' => 'armscii8',
2439
			// Chinese-traditional.
2440
			'big5' => 'big5',
2441
			// Chinese-simplified.
2442
			'gbk' => 'gbk',
2443
			// West European.
2444
			'ISO-8859-1' => 'latin1',
2445
			// Romanian.
2446
			'ISO-8859-2' => 'latin2',
2447
			// Turkish.
2448
			'ISO-8859-9' => 'latin5',
2449
			// Latvian
2450
			'ISO-8859-13' => 'latin7',
2451
			// West European with Euro sign.
2452
			'ISO-8859-15' => 'latin9',
2453
			// Thai.
2454
			'tis-620' => 'tis620',
2455
			// Persian, Chinese, etc.
2456
			'UTF-8' => 'utf8',
2457
			// Russian.
2458
			'windows-1251' => 'cp1251',
2459
			// Greek.
2460
			'windows-1253' => 'utf8',
2461
			// Hebrew.
2462
			'windows-1255' => 'utf8',
2463
			// Arabic.
2464
			'windows-1256' => 'cp1256',
2465
		);
2466
2467
		// Get a list of character sets supported by your MySQL server.
2468
		$request = $smcFunc['db_query']('', '
2469
			SHOW CHARACTER SET',
2470
			array(
2471
			)
2472
		);
2473
		$db_charsets = array();
2474
		while ($row = $smcFunc['db_fetch_assoc']($request))
2475
			$db_charsets[] = $row['Charset'];
2476
2477
		$smcFunc['db_free_result']($request);
2478
2479
		// Character sets supported by both MySQL and SMF's language files.
2480
		$charsets = array_intersect($charsets, $db_charsets);
2481
2482
		// Use the messages.body column as indicator for the database charset.
2483
		$request = $smcFunc['db_query']('', '
2484
			SHOW FULL COLUMNS
2485
			FROM {db_prefix}messages
2486
			LIKE {string:body_like}',
2487
			array(
2488
				'body_like' => 'body',
2489
			)
2490
		);
2491
		$column_info = $smcFunc['db_fetch_assoc']($request);
2492
		$smcFunc['db_free_result']($request);
2493
2494
		// A collation looks like latin1_swedish. We only need the character set.
2495
		list($upcontext['database_charset']) = explode('_', $column_info['Collation']);
2496
		$upcontext['database_charset'] = in_array($upcontext['database_charset'], $charsets) ? array_search($upcontext['database_charset'], $charsets) : $upcontext['database_charset'];
2497
2498
		// Detect whether a fulltext index is set.
2499
		$request = $smcFunc['db_query']('', '
2500
 			SHOW INDEX
2501
	  	    FROM {db_prefix}messages',
2502
			array(
2503
			)
2504
		);
2505
2506
		$upcontext['dropping_index'] = false;
2507
2508
		// If there's a fulltext index, we need to drop it first...
2509 View Code Duplication
		if ($request !== false || $smcFunc['db_num_rows']($request) != 0)
2510
		{
2511
			while ($row = $smcFunc['db_fetch_assoc']($request))
2512
				if ($row['Column_name'] == 'body' && (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT' || isset($row['Comment']) && $row['Comment'] == 'FULLTEXT'))
2513
					$upcontext['fulltext_index'][] = $row['Key_name'];
2514
			$smcFunc['db_free_result']($request);
2515
2516
			if (isset($upcontext['fulltext_index']))
2517
				$upcontext['fulltext_index'] = array_unique($upcontext['fulltext_index']);
2518
		}
2519
2520
		// Drop it and make a note...
2521
		if (!empty($upcontext['fulltext_index']))
2522
		{
2523
			$upcontext['dropping_index'] = true;
2524
2525
			$smcFunc['db_query']('', '
2526
  			ALTER TABLE {db_prefix}messages
2527
	  		DROP INDEX ' . implode(',
2528
		  	DROP INDEX ', $upcontext['fulltext_index']),
2529
				array(
2530
					'db_error_skip' => true,
2531
				)
2532
			);
2533
2534
			// Update the settings table
2535
			$smcFunc['db_insert']('replace',
2536
				'{db_prefix}settings',
2537
				array('variable' => 'string', 'value' => 'string'),
2538
				array('db_search_index', ''),
2539
				array('variable')
2540
			);
2541
		}
2542
2543
		// Figure out what charset we should be converting from...
2544
		$lang_charsets = array(
2545
			'arabic' => 'windows-1256',
2546
			'armenian_east' => 'armscii-8',
2547
			'armenian_west' => 'armscii-8',
2548
			'azerbaijani_latin' => 'ISO-8859-9',
2549
			'bangla' => 'UTF-8',
2550
			'belarusian' => 'ISO-8859-5',
2551
			'bulgarian' => 'windows-1251',
2552
			'cambodian' => 'UTF-8',
2553
			'chinese_simplified' => 'gbk',
2554
			'chinese_traditional' => 'big5',
2555
			'croation' => 'ISO-8859-2',
2556
			'czech' => 'ISO-8859-2',
2557
			'czech_informal' => 'ISO-8859-2',
2558
			'english_pirate' => 'UTF-8',
2559
			'esperanto' => 'ISO-8859-3',
2560
			'estonian' => 'ISO-8859-15',
2561
			'filipino_tagalog' => 'UTF-8',
2562
			'filipino_vasayan' => 'UTF-8',
2563
			'georgian' => 'UTF-8',
2564
			'greek' => 'ISO-8859-3',
2565
			'hebrew' => 'windows-1255',
2566
			'hungarian' => 'ISO-8859-2',
2567
			'irish' => 'UTF-8',
2568
			'japanese' => 'UTF-8',
2569
			'khmer' => 'UTF-8',
2570
			'korean' => 'UTF-8',
2571
			'kurdish_kurmanji' => 'ISO-8859-9',
2572
			'kurdish_sorani' => 'windows-1256',
2573
			'lao' => 'tis-620',
2574
			'latvian' => 'ISO-8859-13',
2575
			'lithuanian' => 'ISO-8859-4',
2576
			'macedonian' => 'UTF-8',
2577
			'malayalam' => 'UTF-8',
2578
			'mongolian' => 'UTF-8',
2579
			'nepali' => 'UTF-8',
2580
			'persian' => 'UTF-8',
2581
			'polish' => 'ISO-8859-2',
2582
			'romanian' => 'ISO-8859-2',
2583
			'russian' => 'windows-1252',
2584
			'sakha' => 'UTF-8',
2585
			'serbian_cyrillic' => 'ISO-8859-5',
2586
			'serbian_latin' => 'ISO-8859-2',
2587
			'sinhala' => 'UTF-8',
2588
			'slovak' => 'ISO-8859-2',
2589
			'slovenian' => 'ISO-8859-2',
2590
			'telugu' => 'UTF-8',
2591
			'thai' => 'tis-620',
2592
			'turkish' => 'ISO-8859-9',
2593
			'turkmen' => 'ISO-8859-9',
2594
			'ukranian' => 'windows-1251',
2595
			'urdu' => 'UTF-8',
2596
			'uzbek_cyrillic' => 'ISO-8859-5',
2597
			'uzbek_latin' => 'ISO-8859-5',
2598
			'vietnamese' => 'UTF-8',
2599
			'yoruba' => 'UTF-8'
2600
		);
2601
2602
		// Default to ISO-8859-1 unless we detected another supported charset
2603
		$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';
2604
2605
		$upcontext['charset_list'] = array_keys($charsets);
2606
2607
		// Translation table for the character sets not native for MySQL.
2608
		$translation_tables = array(
2609
			'windows-1255' => array(
2610
				'0x81' => '\'\'',		'0x8A' => '\'\'',		'0x8C' => '\'\'',
2611
				'0x8D' => '\'\'',		'0x8E' => '\'\'',		'0x8F' => '\'\'',
2612
				'0x90' => '\'\'',		'0x9A' => '\'\'',		'0x9C' => '\'\'',
2613
				'0x9D' => '\'\'',		'0x9E' => '\'\'',		'0x9F' => '\'\'',
2614
				'0xCA' => '\'\'',		'0xD9' => '\'\'',		'0xDA' => '\'\'',
2615
				'0xDB' => '\'\'',		'0xDC' => '\'\'',		'0xDD' => '\'\'',
2616
				'0xDE' => '\'\'',		'0xDF' => '\'\'',		'0xFB' => '0xD792',
2617
				'0xFC' => '0xE282AC',		'0xFF' => '0xD6B2',		'0xC2' => '0xFF',
2618
				'0x80' => '0xFC',		'0xE2' => '0xFB',		'0xA0' => '0xC2A0',
2619
				'0xA1' => '0xC2A1',		'0xA2' => '0xC2A2',		'0xA3' => '0xC2A3',
2620
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2621
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2622
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2623
				'0xAF' => '0xC2AF',		'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',
2624
				'0xB2' => '0xC2B2',		'0xB3' => '0xC2B3',		'0xB4' => '0xC2B4',
2625
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2626
				'0xB8' => '0xC2B8',		'0xB9' => '0xC2B9',		'0xBB' => '0xC2BB',
2627
				'0xBC' => '0xC2BC',		'0xBD' => '0xC2BD',		'0xBE' => '0xC2BE',
2628
				'0xBF' => '0xC2BF',		'0xD7' => '0xD7B3',		'0xD1' => '0xD781',
2629
				'0xD4' => '0xD7B0',		'0xD5' => '0xD7B1',		'0xD6' => '0xD7B2',
2630
				'0xE0' => '0xD790',		'0xEA' => '0xD79A',		'0xEC' => '0xD79C',
2631
				'0xED' => '0xD79D',		'0xEE' => '0xD79E',		'0xEF' => '0xD79F',
2632
				'0xF0' => '0xD7A0',		'0xF1' => '0xD7A1',		'0xF2' => '0xD7A2',
2633
				'0xF3' => '0xD7A3',		'0xF5' => '0xD7A5',		'0xF6' => '0xD7A6',
2634
				'0xF7' => '0xD7A7',		'0xF8' => '0xD7A8',		'0xF9' => '0xD7A9',
2635
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2636
				'0x86' => '0xE280A0',	'0x87' => '0xE280A1',	'0x89' => '0xE280B0',
2637
				'0x8B' => '0xE280B9',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2638
				'0x95' => '0xE280A2',	'0x97' => '0xE28094',	'0x99' => '0xE284A2',
2639
				'0xC0' => '0xD6B0',		'0xC1' => '0xD6B1',		'0xC3' => '0xD6B3',
2640
				'0xC4' => '0xD6B4',		'0xC5' => '0xD6B5',		'0xC6' => '0xD6B6',
2641
				'0xC7' => '0xD6B7',		'0xC8' => '0xD6B8',		'0xC9' => '0xD6B9',
2642
				'0xCB' => '0xD6BB',		'0xCC' => '0xD6BC',		'0xCD' => '0xD6BD',
2643
				'0xCE' => '0xD6BE',		'0xCF' => '0xD6BF',		'0xD0' => '0xD780',
2644
				'0xD2' => '0xD782',		'0xE3' => '0xD793',		'0xE4' => '0xD794',
2645
				'0xE5' => '0xD795',		'0xE7' => '0xD797',		'0xE9' => '0xD799',
2646
				'0xFD' => '0xE2808E',	'0xFE' => '0xE2808F',	'0x92' => '0xE28099',
2647
				'0x83' => '0xC692',		'0xD3' => '0xD783',		'0x88' => '0xCB86',
2648
				'0x98' => '0xCB9C',		'0x91' => '0xE28098',	'0x96' => '0xE28093',
2649
				'0xBA' => '0xC3B7',		'0x9B' => '0xE280BA',	'0xAA' => '0xC397',
2650
				'0xA4' => '0xE282AA',	'0xE1' => '0xD791',		'0xE6' => '0xD796',
2651
				'0xE8' => '0xD798',		'0xEB' => '0xD79B',		'0xF4' => '0xD7A4',
2652
				'0xFA' => '0xD7AA',
2653
			),
2654
			'windows-1253' => array(
2655
				'0x81' => '\'\'',			'0x88' => '\'\'',			'0x8A' => '\'\'',
2656
				'0x8C' => '\'\'',			'0x8D' => '\'\'',			'0x8E' => '\'\'',
2657
				'0x8F' => '\'\'',			'0x90' => '\'\'',			'0x98' => '\'\'',
2658
				'0x9A' => '\'\'',			'0x9C' => '\'\'',			'0x9D' => '\'\'',
2659
				'0x9E' => '\'\'',			'0x9F' => '\'\'',			'0xAA' => '\'\'',
2660
				'0xD2' => '0xE282AC',			'0xFF' => '0xCE92',			'0xCE' => '0xCE9E',
2661
				'0xB8' => '0xCE88',		'0xBA' => '0xCE8A',		'0xBC' => '0xCE8C',
2662
				'0xBE' => '0xCE8E',		'0xBF' => '0xCE8F',		'0xC0' => '0xCE90',
2663
				'0xC8' => '0xCE98',		'0xCA' => '0xCE9A',		'0xCC' => '0xCE9C',
2664
				'0xCD' => '0xCE9D',		'0xCF' => '0xCE9F',		'0xDA' => '0xCEAA',
2665
				'0xE8' => '0xCEB8',		'0xEA' => '0xCEBA',		'0xEC' => '0xCEBC',
2666
				'0xEE' => '0xCEBE',		'0xEF' => '0xCEBF',		'0xC2' => '0xFF',
2667
				'0xBD' => '0xC2BD',		'0xED' => '0xCEBD',		'0xB2' => '0xC2B2',
2668
				'0xA0' => '0xC2A0',		'0xA3' => '0xC2A3',		'0xA4' => '0xC2A4',
2669
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2670
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2671
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2672
				'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',		'0xB3' => '0xC2B3',
2673
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2674
				'0xBB' => '0xC2BB',		'0xE2' => '0xCEB2',		'0x80' => '0xD2',
2675
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2676
				'0x86' => '0xE280A0',	'0xA1' => '0xCE85',		'0xA2' => '0xCE86',
2677
				'0x87' => '0xE280A1',	'0x89' => '0xE280B0',	'0xB9' => '0xCE89',
2678
				'0x8B' => '0xE280B9',	'0x91' => '0xE28098',	'0x99' => '0xE284A2',
2679
				'0x92' => '0xE28099',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2680
				'0x95' => '0xE280A2',	'0x96' => '0xE28093',	'0x97' => '0xE28094',
2681
				'0x9B' => '0xE280BA',	'0xAF' => '0xE28095',	'0xB4' => '0xCE84',
2682
				'0xC1' => '0xCE91',		'0xC3' => '0xCE93',		'0xC4' => '0xCE94',
2683
				'0xC5' => '0xCE95',		'0xC6' => '0xCE96',		'0x83' => '0xC692',
2684
				'0xC7' => '0xCE97',		'0xC9' => '0xCE99',		'0xCB' => '0xCE9B',
2685
				'0xD0' => '0xCEA0',		'0xD1' => '0xCEA1',		'0xD3' => '0xCEA3',
2686
				'0xD4' => '0xCEA4',		'0xD5' => '0xCEA5',		'0xD6' => '0xCEA6',
2687
				'0xD7' => '0xCEA7',		'0xD8' => '0xCEA8',		'0xD9' => '0xCEA9',
2688
				'0xDB' => '0xCEAB',		'0xDC' => '0xCEAC',		'0xDD' => '0xCEAD',
2689
				'0xDE' => '0xCEAE',		'0xDF' => '0xCEAF',		'0xE0' => '0xCEB0',
2690
				'0xE1' => '0xCEB1',		'0xE3' => '0xCEB3',		'0xE4' => '0xCEB4',
2691
				'0xE5' => '0xCEB5',		'0xE6' => '0xCEB6',		'0xE7' => '0xCEB7',
2692
				'0xE9' => '0xCEB9',		'0xEB' => '0xCEBB',		'0xF0' => '0xCF80',
2693
				'0xF1' => '0xCF81',		'0xF2' => '0xCF82',		'0xF3' => '0xCF83',
2694
				'0xF4' => '0xCF84',		'0xF5' => '0xCF85',		'0xF6' => '0xCF86',
2695
				'0xF7' => '0xCF87',		'0xF8' => '0xCF88',		'0xF9' => '0xCF89',
2696
				'0xFA' => '0xCF8A',		'0xFB' => '0xCF8B',		'0xFC' => '0xCF8C',
2697
				'0xFD' => '0xCF8D',		'0xFE' => '0xCF8E',
2698
			),
2699
		);
2700
2701
		// Make some preparations.
2702
		if (isset($translation_tables[$upcontext['charset_detected']]))
2703
		{
2704
			$replace = '%field%';
2705
2706
			// Build a huge REPLACE statement...
2707
			foreach ($translation_tables[$upcontext['charset_detected']] as $from => $to)
2708
				$replace = 'REPLACE(' . $replace . ', ' . $from . ', ' . $to . ')';
2709
		}
2710
2711
		// Get a list of table names ahead of time... This makes it easier to set our substep and such
2712
		db_extend();
2713
		$queryTables = $smcFunc['db_list_tables'](false, $db_prefix . '%');
2714
2715
		$upcontext['table_count'] = count($queryTables);
2716
2717
		// We want to start at the first table.
2718
		for ($substep = ($_GET['substep'] == 0 ? 1 : $_GET['substep']); $substep <= $upcontext['table_count']; $substep++)
2719
		{
2720
			$table = $queryTables[$_GET['substep']];
2721
2722
			// Do we need to pause?
2723
			nextSubstep($substep);
2724
2725
			$getTableStatus = $smcFunc['db_query']('', '
2726
				SHOW TABLE STATUS
2727
				LIKE {string:table_name}',
2728
				array(
2729
					'table_name' => str_replace('_', '\_', $table)
2730
				)
2731
			);
2732
2733
			// Only one row so we can just fetch_assoc and free the result...
2734
			$table_info = $smcFunc['db_fetch_assoc']($getTableStatus);
2735
			$smcFunc['db_free_result']($getTableStatus);
2736
2737
			$upcontext['cur_table_num'] = $_GET['substep'];
2738
			$upcontext['cur_table_name'] = $table_info['Name'];
2739
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2740
2741
			// Just to make sure it doesn't time out.
2742
			if (function_exists('apache_reset_timeout'))
2743
				@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...
2744
2745
			$table_charsets = array();
2746
2747
			// Loop through each column.
2748
			$queryColumns = $smcFunc['db_query']('', '
2749
				SHOW FULL COLUMNS
2750
				FROM ' . $table_info['Name'],
2751
				array(
2752
				)
2753
			);
2754
			while ($column_info = $smcFunc['db_fetch_assoc']($queryColumns))
2755
			{
2756
				// Only text'ish columns have a character set and need converting.
2757
				if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false)
2758
				{
2759
					$collation = empty($column_info['Collation']) || $column_info['Collation'] === 'NULL' ? $table_info['Collation'] : $column_info['Collation'];
2760
					if (!empty($collation) && $collation !== 'NULL')
2761
					{
2762
						list($charset) = explode('_', $collation);
2763
2764
						if (!isset($table_charsets[$charset]))
2765
							$table_charsets[$charset] = array();
2766
2767
						$table_charsets[$charset][] = $column_info;
2768
					}
2769
				}
2770
			}
2771
			$smcFunc['db_free_result']($queryColumns);
2772
2773
			// Only change the column if the data doesn't match the current charset.
2774
			if ((count($table_charsets) === 1 && key($table_charsets) !== $charsets[$upcontext['charset_detected']]) || count($table_charsets) > 1)
2775
			{
2776
				$updates_blob = '';
2777
				$updates_text = '';
2778
				foreach ($table_charsets as $charset => $columns)
2779
				{
2780
					if ($charset !== $charsets[$upcontext['charset_detected']])
2781
					{
2782
						foreach ($columns as $column)
2783
						{
2784
							$updates_blob .= '
2785
								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'] . '\'') . ',';
2786
							$updates_text .= '
2787
								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'] . '\'') . ',';
2788
						}
2789
					}
2790
				}
2791
2792
				// Change the columns to binary form.
2793
				$smcFunc['db_query']('', '
2794
					ALTER TABLE {raw:table_name}{raw:updates_blob}',
2795
					array(
2796
						'table_name' => $table_info['Name'],
2797
						'updates_blob' => substr($updates_blob, 0, -1),
2798
					)
2799
				);
2800
2801
				// Convert the character set if MySQL has no native support for it.
2802
				if (isset($translation_tables[$upcontext['charset_detected']]))
2803
				{
2804
					$update = '';
2805
					foreach ($table_charsets as $charset => $columns)
2806
						foreach ($columns as $column)
2807
							$update .= '
2808
								' . $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...
2809
2810
					$smcFunc['db_query']('', '
2811
						UPDATE {raw:table_name}
2812
						SET {raw:updates}',
2813
						array(
2814
							'table_name' => $table_info['Name'],
2815
							'updates' => substr($update, 0, -1),
2816
						)
2817
					);
2818
				}
2819
2820
				// Change the columns back, but with the proper character set.
2821
				$smcFunc['db_query']('', '
2822
					ALTER TABLE {raw:table_name}{raw:updates_text}',
2823
					array(
2824
						'table_name' => $table_info['Name'],
2825
						'updates_text' => substr($updates_text, 0, -1),
2826
					)
2827
				);
2828
			}
2829
2830
			// Now do the actual conversion (if still needed).
2831
			if ($charsets[$upcontext['charset_detected']] !== 'utf8')
2832
			{
2833
				if ($command_line)
2834
					echo 'Converting table ' . $table_info['Name'] . ' to UTF-8...';
2835
2836
				$smcFunc['db_query']('', '
2837
					ALTER TABLE {raw:table_name}
2838
					CONVERT TO CHARACTER SET utf8',
2839
					array(
2840
						'table_name' => $table_info['Name'],
2841
					)
2842
				);
2843
2844
				if ($command_line)
2845
					echo " done.\n";
2846
			}
2847
		}
2848
2849
		$prev_charset = empty($translation_tables[$upcontext['charset_detected']]) ? $charsets[$upcontext['charset_detected']] : $translation_tables[$upcontext['charset_detected']];
2850
2851
		$smcFunc['db_insert']('replace',
2852
			'{db_prefix}settings',
2853
			array('variable' => 'string', 'value' => 'string'),
2854
			array(array('global_character_set', 'UTF-8'), array('previousCharacterSet', $prev_charset)),
2855
			array('variable')
2856
		);
2857
2858
		// Store it in Settings.php too because it's needed before db connection.
2859
		// Hopefully this works...
2860
		require_once($sourcedir . '/Subs-Admin.php');
2861
		updateSettingsFile(array('db_character_set' => '\'utf8\''));
2862
2863
		// The conversion might have messed up some serialized strings. Fix them!
2864
		$request = $smcFunc['db_query']('', '
2865
			SELECT id_action, extra
2866
			FROM {db_prefix}log_actions
2867
			WHERE action IN ({string:remove}, {string:delete})',
2868
			array(
2869
				'remove' => 'remove',
2870
				'delete' => 'delete',
2871
			)
2872
		);
2873 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
2874
		{
2875
			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)
2876
				$smcFunc['db_query']('', '
2877
					UPDATE {db_prefix}log_actions
2878
					SET extra = {string:extra}
2879
					WHERE id_action = {int:current_action}',
2880
					array(
2881
						'current_action' => $row['id_action'],
2882
						'extra' => $matches[1] . strlen($matches[3]) . ':"' . $matches[3] . '"' . $matches[4],
2883
					)
2884
				);
2885
		}
2886
		$smcFunc['db_free_result']($request);
2887
2888
		if ($upcontext['dropping_index'] && $command_line)
2889
		{
2890
			echo "\nYour fulltext search index was dropped to facilitate the conversion. You will need to recreate it.";
2891
			flush();
2892
		}
2893
	}
2894
2895
	return true;
2896
}
2897
2898
function serialize_to_json()
2899
{
2900
	global $command_line, $smcFunc, $modSettings, $sourcedir, $upcontext, $support_js;
2901
2902
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'serialize_json_xml' : 'serialize_json';
2903
	// First thing's first - did we already do this?
2904
	if (!empty($modSettings['json_done']))
2905
	{
2906
		if ($command_line)
2907
			return DeleteUpgrade();
2908
		else
2909
			return true;
2910
	}
2911
2912
	// Done it already - js wise?
2913
	if (!empty($_POST['json_done']))
2914
		return true;
2915
2916
	// List of tables affected by this function
2917
	// 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...
2918
	// If 3rd item in array is true, it indicates that col1 could be empty...
2919
	$tables = array(
2920
		'background_tasks' => array('id_task', 'task_data'),
2921
		'log_actions' => array('id_action', 'extra'),
2922
		'log_online' => array('session', 'url'),
2923
		'log_packages' => array('id_install', 'db_changes', 'failed_steps', 'credits'),
2924
		'log_spider_hits' => array('id_hit', 'url'),
2925
		'log_subscribed' => array('id_sublog', 'pending_details'),
2926
		'pm_rules' => array('id_rule', 'criteria', 'actions'),
2927
		'qanda' => array('id_question', 'answers'),
2928
		'subscriptions' => array('id_subscribe', 'cost'),
2929
		'user_alerts' => array('id_alert', 'extra', true),
2930
		'user_drafts' => array('id_draft', 'to_list', true),
2931
		// These last two are a bit different - we'll handle those separately
2932
		'settings' => array(),
2933
		'themes' => array()
2934
	);
2935
2936
	// Set up some context stuff...
2937
	// Because we're not using numeric indices, we need this to figure out the current table name...
2938
	$keys = array_keys($tables);
2939
2940
	$upcontext['table_count'] = count($keys);
2941
	$upcontext['cur_table_num'] = $_GET['substep'];
2942
	$upcontext['cur_table_name'] = isset($keys[$_GET['substep']]) ? $keys[$_GET['substep']] : $keys[0];
2943
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2944
2945 View Code Duplication
	foreach ($keys as $id => $table)
2946
		if ($id < $_GET['substep'])
2947
			$upcontext['previous_tables'][] = $table;
2948
2949
	if ($command_line)
2950
		echo 'Converting data from serialize() to json_encode().';
2951
2952
	if (!$support_js || isset($_GET['xml']))
2953
	{
2954
		// Fix the data in each table
2955
		for ($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
2956
		{
2957
			$upcontext['cur_table_name'] = isset($keys[$substep + 1]) ? $keys[$substep + 1] : $keys[$substep];
2958
			$upcontext['cur_table_num'] = $substep + 1;
2959
2960
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2961
2962
			// Do we need to pause?
2963
			nextSubstep($substep);
2964
2965
			// Initialize a few things...
2966
			$where = '';
2967
			$vars = array();
2968
			$table = $keys[$substep];
2969
			$info = $tables[$table];
2970
2971
			// Now the fun - build our queries and all that fun stuff
2972
			if ($table == 'settings')
2973
			{
2974
				// Now a few settings...
2975
				$serialized_settings = array(
2976
					'attachment_basedirectories',
2977
					'attachmentUploadDir',
2978
					'cal_today_birthday',
2979
					'cal_today_event',
2980
					'cal_today_holiday',
2981
					'displayFields',
2982
					'last_attachments_directory',
2983
					'memberlist_cache',
2984
					'search_index_custom_config',
2985
					'spider_name_cache'
2986
				);
2987
2988
				// Loop through and fix these...
2989
				$new_settings = array();
2990
				if ($command_line)
2991
					echo "\n" . 'Fixing some settings...';
2992
2993
				foreach ($serialized_settings as $var)
2994
				{
2995
					if (isset($modSettings[$var]))
2996
					{
2997
						// Attempt to unserialize the setting
2998
						$temp = @safe_unserialize($modSettings[$var]);
2999
						if (!$temp && $command_line)
3000
							echo "\n - Failed to unserialize the '" . $var . "' setting. Skipping.";
3001
						elseif ($temp !== false)
3002
							$new_settings[$var] = json_encode($temp);
3003
					}
3004
				}
3005
3006
				// Update everything at once
3007
				if (!function_exists('cache_put_data'))
3008
					require_once($sourcedir . '/Load.php');
3009
				updateSettings($new_settings, true);
3010
3011
				if ($command_line)
3012
					echo ' done.';
3013
			}
3014
			elseif ($table == 'themes')
3015
			{
3016
				// Finally, fix the admin prefs. Unfortunately this is stored per theme, but hopefully they only have one theme installed at this point...
3017
				$query = $smcFunc['db_query']('', '
3018
					SELECT id_member, id_theme, value FROM {db_prefix}themes
3019
					WHERE variable = {string:admin_prefs}',
3020
						array(
3021
							'admin_prefs' => 'admin_preferences'
3022
						)
3023
				);
3024
3025
				if ($smcFunc['db_num_rows']($query) != 0)
3026
				{
3027
					while ($row = $smcFunc['db_fetch_assoc']($query))
3028
					{
3029
						$temp = @safe_unserialize($row['value']);
3030
3031
						if ($command_line)
3032
						{
3033
							if ($temp === false)
3034
								echo "\n" . 'Unserialize of admin_preferences for user ' . $row['id_member'] . ' failed. Skipping.';
3035
							else
3036
								echo "\n" . 'Fixing admin preferences...';
3037
						}
3038
3039
						if ($temp !== false)
3040
						{
3041
							$row['value'] = json_encode($temp);
3042
3043
							// Even though we have all values from the table, UPDATE is still faster than REPLACE
3044
							$smcFunc['db_query']('', '
3045
								UPDATE {db_prefix}themes
3046
								SET value = {string:prefs}
3047
								WHERE id_theme = {int:theme}
3048
									AND id_member = {int:member}',
3049
								array(
3050
									'prefs' => $row['value'],
3051
									'theme' => $row['id_theme'],
3052
									'member' => $row['id_member']
3053
								)
3054
							);
3055
3056
							if ($command_line)
3057
								echo ' done.';
3058
						}
3059
					}
3060
3061
					$smcFunc['db_free_result']($query);
3062
				}
3063
			}
3064
			else
3065
			{
3066
				// First item is always the key...
3067
				$key = $info[0];
3068
				unset($info[0]);
3069
3070
				// Now we know what columns we have and such...
3071
				if (count($info) == 2 && $info[2] === true)
3072
				{
3073
					$col_select = $info[1];
3074
					$where = ' WHERE ' . $info[1] . ' != {empty}';
3075
				}
3076
				else
3077
				{
3078
					$col_select = implode(', ', $info);
3079
				}
3080
3081
				$query = $smcFunc['db_query']('', '
3082
					SELECT ' . $key . ', ' . $col_select . '
3083
					FROM {db_prefix}' . $table . $where,
3084
					array()
3085
				);
3086
3087
				if ($smcFunc['db_num_rows']($query) != 0)
3088
				{
3089
					if ($command_line)
3090
					{
3091
						echo "\n" . ' +++ Fixing the "' . $table . '" table...';
3092
						flush();
3093
					}
3094
3095
					while ($row = $smcFunc['db_fetch_assoc']($query))
3096
					{
3097
						$update = '';
3098
3099
						// We already know what our key is...
3100
						foreach ($info as $col)
3101
						{
3102
							if ($col !== true && $row[$col] != '')
3103
							{
3104
								$temp = @safe_unserialize($row[$col]);
3105
3106
								if ($temp === false && $command_line)
3107
								{
3108
									echo "\nFailed to unserialize " . $row[$col] . "... Skipping\n";
3109
								}
3110
								else
3111
								{
3112
									$row[$col] = json_encode($temp);
3113
3114
									// Build our SET string and variables array
3115
									$update .= (empty($update) ? '' : ', ') . $col . ' = {string:' . $col . '}';
3116
									$vars[$col] = $row[$col];
3117
								}
3118
							}
3119
						}
3120
3121
						$vars[$key] = $row[$key];
3122
3123
						// In a few cases, we might have empty data, so don't try to update in those situations...
3124
						if (!empty($update))
3125
						{
3126
							$smcFunc['db_query']('', '
3127
								UPDATE {db_prefix}' . $table . '
3128
								SET ' . $update . '
3129
								WHERE ' . $key . ' = {' . ($key == 'session' ? 'string' : 'int') . ':' . $key . '}',
3130
								$vars
3131
							);
3132
						}
3133
					}
3134
3135
					if ($command_line)
3136
						echo ' done.';
3137
3138
					// Free up some memory...
3139
					$smcFunc['db_free_result']($query);
3140
				}
3141
			}
3142
			// If this is XML to keep it nice for the user do one table at a time anyway!
3143
			if (isset($_GET['xml']))
3144
				return upgradeExit();
3145
		}
3146
3147
		if ($command_line)
3148
		{
3149
			echo "\n" . 'Successful.' . "\n";
3150
			flush();
3151
		}
3152
		$upcontext['step_progress'] = 100;
3153
3154
		// Last but not least, insert a dummy setting so we don't have to do this again in the future...
3155
		updateSettings(array('json_done' => true));
3156
3157
		$_GET['substep'] = 0;
3158
		// Make sure we move on!
3159
		if ($command_line)
3160
			return DeleteUpgrade();
3161
3162
		return true;
3163
	}
3164
3165
	// If this fails we just move on to deleting the upgrade anyway...
3166
	$_GET['substep'] = 0;
3167
	return false;
3168
}
3169
3170
/******************************************************************************
3171
******************* Templates are below this point ****************************
3172
******************************************************************************/
3173
3174
// This is what is displayed if there's any chmod to be done. If not it returns nothing...
3175
function template_chmod()
3176
{
3177
	global $upcontext, $txt, $settings;
3178
3179
	// Don't call me twice!
3180
	if (!empty($upcontext['chmod_called']))
3181
		return;
3182
3183
	$upcontext['chmod_called'] = true;
3184
3185
	// Nothing?
3186
	if (empty($upcontext['chmod']['files']) && empty($upcontext['chmod']['ftp_error']))
3187
		return;
3188
3189
	// Was it a problem with Windows?
3190
	if (!empty($upcontext['chmod']['ftp_error']) && $upcontext['chmod']['ftp_error'] == 'total_mess')
3191
	{
3192
		echo '
3193
			<div class="error_message red">
3194
				The following files need to be writable to continue the upgrade. Please ensure the Windows permissions are correctly set to allow this:<br>
3195
				<ul style="margin: 2.5ex; font-family: monospace;">
3196
					<li>' . implode('</li>
3197
					<li>', $upcontext['chmod']['files']) . '</li>
3198
				</ul>
3199
			</div>';
3200
3201
		return false;
3202
	}
3203
3204
	echo '
3205
		<div class="panel">
3206
			<h2>Your FTP connection information</h2>
3207
			<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>
3208
			<script>
3209
				function warning_popup()
3210
				{
3211
					popup = window.open(\'\',\'popup\',\'height=150,width=400,scrollbars=yes\');
3212
					var content = popup.document;
3213
					content.write(\'<!DOCTYPE html>\n\');
3214
					content.write(\'<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>\n\t<head>\n\t\t<meta name="robots" content="noindex">\n\t\t\');
3215
					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\');
3216
					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\');
3217
					content.write(\'<p>', implode('<br>\n\t\t\t', $upcontext['chmod']['files']), '</p>\n\t\t\t\');';
3218
3219
	if (isset($upcontext['systemos']) && $upcontext['systemos'] == 'linux')
3220
		echo '
3221
					content.write(\'<hr>\n\t\t\t\');
3222
					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\');
3223
					content.write(\'<tt># chmod a+w ', implode(' ', $upcontext['chmod']['files']), '</tt>\n\t\t\t\');';
3224
3225
	echo '
3226
					content.write(\'<a href="javascript:self.close();">close</a>\n\t\t</div>\n\t</body>\n</html>\');
3227
					content.close();
3228
				}
3229
		</script>';
3230
3231
	if (!empty($upcontext['chmod']['ftp_error']))
3232
		echo '
3233
			<div class="error_message red">
3234
				The following error was encountered when trying to connect:<br><br>
3235
				<code>', $upcontext['chmod']['ftp_error'], '</code>
3236
			</div>
3237
			<br>';
3238
3239
	if (empty($upcontext['chmod_in_form']))
3240
		echo '
3241
	<form action="', $upcontext['form_url'], '" method="post">';
3242
3243
	echo '
3244
		<table width="520" border="0" align="center" style="margin-bottom: 1ex;">
3245
			<tr>
3246
				<td width="26%" valign="top" class="textbox"><label for="ftp_server">', $txt['ftp_server'], ':</label></td>
3247
				<td>
3248
					<div style="float: right; margin-right: 1px;"><label for="ftp_port" class="textbox"><strong>', $txt['ftp_port'], ':&nbsp;</strong></label> <input type="text" size="3" name="ftp_port" id="ftp_port" value="', isset($upcontext['chmod']['port']) ? $upcontext['chmod']['port'] : '21', '" class="input_text"></div>
3249
					<input type="text" size="30" name="ftp_server" id="ftp_server" value="', isset($upcontext['chmod']['server']) ? $upcontext['chmod']['server'] : 'localhost', '" style="width: 70%;" class="input_text">
3250
					<div class="smalltext block">', $txt['ftp_server_info'], '</div>
3251
				</td>
3252
			</tr><tr>
3253
				<td width="26%" valign="top" class="textbox"><label for="ftp_username">', $txt['ftp_username'], ':</label></td>
3254
				<td>
3255
					<input type="text" size="50" name="ftp_username" id="ftp_username" value="', isset($upcontext['chmod']['username']) ? $upcontext['chmod']['username'] : '', '" style="width: 99%;" class="input_text">
3256
					<div class="smalltext block">', $txt['ftp_username_info'], '</div>
3257
				</td>
3258
			</tr><tr>
3259
				<td width="26%" valign="top" class="textbox"><label for="ftp_password">', $txt['ftp_password'], ':</label></td>
3260
				<td>
3261
					<input type="password" size="50" name="ftp_password" id="ftp_password" style="width: 99%;" class="input_password">
3262
					<div class="smalltext block">', $txt['ftp_password_info'], '</div>
3263
				</td>
3264
			</tr><tr>
3265
				<td width="26%" valign="top" class="textbox"><label for="ftp_path">', $txt['ftp_path'], ':</label></td>
3266
				<td style="padding-bottom: 1ex;">
3267
					<input type="text" size="50" name="ftp_path" id="ftp_path" value="', isset($upcontext['chmod']['path']) ? $upcontext['chmod']['path'] : '', '" style="width: 99%;" class="input_text">
3268
					<div class="smalltext block">', !empty($upcontext['chmod']['path']) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'], '</div>
3269
				</td>
3270
			</tr>
3271
		</table>
3272
3273
		<div class="righttext" style="margin: 1ex;"><input type="submit" value="', $txt['ftp_connect'], '" class="button_submit"></div>
3274
	</div>';
3275
3276
	if (empty($upcontext['chmod_in_form']))
3277
		echo '
3278
	</form>';
3279
}
3280
3281
function template_upgrade_above()
3282
{
3283
	global $modSettings, $txt, $settings, $upcontext, $upgradeurl;
3284
3285
	echo '<!DOCTYPE html>
3286
<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>
3287
	<head>
3288
		<meta charset="', isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8', '">
3289
		<meta name="robots" content="noindex">
3290
		<title>', $txt['upgrade_upgrade_utility'], '</title>
3291
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/index.css?alp21">
3292
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/install.css?alp21">
3293
		', $txt['lang_rtl'] == true ? '<link rel="stylesheet" href="' . $settings['default_theme_url'] . '/css/rtl.css?alp21">' : '', '
3294
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
3295
		<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
3296
		<script>
3297
			var smf_scripturl = \'', $upgradeurl, '\';
3298
			var smf_charset = \'', (empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'UTF-8' : $txt['lang_character_set']) : $modSettings['global_character_set']), '\';
3299
			var startPercent = ', $upcontext['overall_percent'], ';
3300
3301
			// This function dynamically updates the step progress bar - and overall one as required.
3302
			function updateStepProgress(current, max, overall_weight)
3303
			{
3304
				// What out the actual percent.
3305
				var width = parseInt((current / max) * 100);
3306
				if (document.getElementById(\'step_progress\'))
3307
				{
3308
					document.getElementById(\'step_progress\').style.width = width + "%";
3309
					setInnerHTML(document.getElementById(\'step_text\'), width + "%");
3310
				}
3311
				if (overall_weight && document.getElementById(\'overall_progress\'))
3312
				{
3313
					overall_width = parseInt(startPercent + width * (overall_weight / 100));
3314
					document.getElementById(\'overall_progress\').style.width = overall_width + "%";
3315
					setInnerHTML(document.getElementById(\'overall_text\'), overall_width + "%");
3316
				}
3317
			}
3318
		</script>
3319
	</head>
3320
	<body>
3321
	<div id="footerfix">
3322
		<div id="header">
3323
			<h1 class="forumtitle">', $txt['upgrade_upgrade_utility'], '</h1>
3324
			<img id="smflogo" src="', $settings['default_theme_url'], '/images/smflogo.png" alt="Simple Machines Forum" title="Simple Machines Forum">
3325
		</div>
3326
	<div id="wrapper">
3327
		<div id="upper_section">
3328
			<div id="inner_section">
3329
				<div id="inner_wrap">
3330
				</div>
3331
			</div>
3332
		</div>
3333
		<div id="content_section">
3334
		<div id="main_content_section">
3335
			<div id="main_steps">
3336
				<h2>', $txt['upgrade_progress'], '</h2>
3337
				<ul>';
3338
3339 View Code Duplication
	foreach ($upcontext['steps'] as $num => $step)
3340
		echo '
3341
						<li class="', $num < $upcontext['current_step'] ? 'stepdone' : ($num == $upcontext['current_step'] ? 'stepcurrent' : 'stepwaiting'), '">', $txt['upgrade_step'], ' ', $step[0], ': ', $step[1], '</li>';
3342
3343
	echo '
3344
					</ul>
3345
			</div>
3346
3347
			<div id="progress_bar">
3348
				<div id="overall_text">', $upcontext['overall_percent'], '%</div>
3349
				<div id="overall_progress" style="width: ', $upcontext['overall_percent'], '%;">
3350
					<span>', $txt['upgrade_overall_progress'], '</span>
3351
				</div>
3352
			</div>';
3353
3354
	if (isset($upcontext['step_progress']))
3355
		echo '
3356
				<br>
3357
				<br>
3358
				<div id="progress_bar_step">
3359
					<div id="step_text">', $upcontext['step_progress'], '%</div>
3360
					<div id="step_progress" style="width: ', $upcontext['step_progress'], '%;background-color: #ffd000;">
3361
						<span>', $txt['upgrade_step_progress'], '</span>
3362
					</div>
3363
				</div>';
3364
3365
	echo '
3366
				<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>
3367
				<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', ';">
3368
					<div id="substep_text" style="color: #000; position: absolute; margin-left: -5em;">', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : '', '%</div>
3369
					<div id="substep_progress" style="width: ', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%; height: 12pt; z-index: 1; background-color: #eebaf4;">&nbsp;</div>
3370
				</div>';
3371
3372
	// How long have we been running this?
3373
	$elapsed = time() - $upcontext['started'];
3374
	$mins = (int) ($elapsed / 60);
3375
	$seconds = $elapsed - $mins * 60;
3376
	echo '
3377
								<br> <br> <br> <br> <br>
3378
								<div class="smalltext" style="padding: 5px; text-align: center;"><br>', $txt['upgrade_time_elapsed'], ':
3379
									<span id="mins_elapsed">', $mins, '</span> ', $txt['upgrade_time_mins'], ', <span id="secs_elapsed">', $seconds, '</span> ', $txt['upgrade_time_secs'], '.
3380
								</div>';
3381
	echo '
3382
			</div>
3383
			</div>
3384
			<div id="main_screen" class="clear">
3385
				<h2>', $upcontext['page_title'], '</h2>
3386
				<div class="panel">
3387
					<div style="max-height: 360px; overflow: auto;">';
3388
}
3389
3390
function template_upgrade_below()
3391
{
3392
	global $upcontext, $txt;
3393
3394
	if (!empty($upcontext['pause']))
3395
		echo '
3396
								<em>', $txt['upgrade_incomplete'], '.</em><br>
3397
3398
								<h2 style="margin-top: 2ex;">', $txt['upgrade_not_quite_done'], '</h2>
3399
								<h3>
3400
									', $txt['upgrade_paused_overload'], '
3401
								</h3>';
3402
3403
	if (!empty($upcontext['custom_warning']))
3404
		echo '
3405
								<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3406
									<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3407
									<strong style="text-decoration: underline;">', $txt['upgrade_note'], '</strong><br>
3408
									<div style="padding-left: 6ex;">', $upcontext['custom_warning'], '</div>
3409
								</div>';
3410
3411
	echo '
3412
								<div class="righttext" style="margin: 1ex;">';
3413
3414
	if (!empty($upcontext['continue']))
3415
		echo '
3416
									<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '"', $upcontext['continue'] == 2 ? ' disabled' : '', ' class="button_submit">';
3417
	if (!empty($upcontext['skip']))
3418
		echo '
3419
									<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="dontSubmit = true; document.getElementById(\'contbutt\').disabled = \'disabled\'; return true;" class="button_submit">';
3420
3421
	echo '
3422
								</div>
3423
							</form>
3424
						</div>
3425
				</div>
3426
			</div>
3427
			</div>
3428
		</div>
3429
		<div id="footer">
3430
			<ul>
3431
				<li class="copyright"><a href="http://www.simplemachines.org/" title="Simple Machines Forum" target="_blank" class="new_win">SMF &copy; 2016, Simple Machines</a></li>
3432
			</ul>
3433
		</div>
3434
	</body>
3435
</html>';
3436
3437
	// Are we on a pause?
3438
	if (!empty($upcontext['pause']))
3439
	{
3440
		echo '
3441
		<script>
3442
			window.onload = doAutoSubmit;
3443
			var countdown = 3;
3444
			var dontSubmit = false;
3445
3446
			function doAutoSubmit()
3447
			{
3448
				if (countdown == 0 && !dontSubmit)
3449
					document.upform.submit();
3450
				else if (countdown == -1)
3451
					return;
3452
3453
				document.getElementById(\'contbutt\').value = "', $txt['upgrade_continue'], ' (" + countdown + ")";
3454
				countdown--;
3455
3456
				setTimeout("doAutoSubmit();", 1000);
3457
			}
3458
		</script>';
3459
	}
3460
}
3461
3462
function template_xml_above()
3463
{
3464
	global $upcontext;
3465
3466
	echo '<', '?xml version="1.0" encoding="UTF-8"?', '>
3467
	<smf>';
3468
3469
	if (!empty($upcontext['get_data']))
3470
		foreach ($upcontext['get_data'] as $k => $v)
3471
			echo '
3472
		<get key="', $k, '">', $v, '</get>';
3473
}
3474
3475
function template_xml_below()
3476
{
3477
	echo '
3478
		</smf>';
3479
}
3480
3481
function template_error_message()
3482
{
3483
	global $upcontext;
3484
3485
	echo '
3486
	<div class="error_message red">
3487
		', $upcontext['error_msg'], '
3488
		<br>
3489
		<a href="', $_SERVER['PHP_SELF'], '">Click here to try again.</a>
0 ignored issues
show
Security Cross-Site Scripting introduced by
$_SERVER['PHP_SELF'] can contain request data and is used in output context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Fetching key PHP_SELF from $_SERVER
    in other/upgrade.php on line 3489

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
3490
	</div>';
3491
}
3492
3493
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 (L1899-1948) 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...
3494
{
3495
	global $upcontext, $disable_security, $settings, $txt;
3496
3497
	echo '
3498
		<script src="http://www.simplemachines.org/smf/current-version.js?version=' . SMF_VERSION . '"></script>
3499
			<h3>', sprintf($txt['upgrade_ready_proceed'], SMF_VERSION), '</h3>
3500
	<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">
3501
		<input type="hidden" name="', $upcontext['login_token_var'], '" value="', $upcontext['login_token'], '">
3502
		<div id="version_warning" style="margin: 2ex; padding: 2ex; border: 2px dashed #a92174; color: black; background-color: #fbbbe2; display: none;">
3503
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3504
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3505
			<div style="padding-left: 6ex;">
3506
				', sprintf($txt['upgrade_warning_out_of_date'], SMF_VERSION), '
3507
			</div>
3508
		</div>';
3509
3510
	$upcontext['chmod_in_form'] = true;
3511
	template_chmod();
3512
3513
	// For large, pre 1.1 RC2 forums give them a warning about the possible impact of this upgrade!
3514
	if ($upcontext['is_large_forum'])
3515
		echo '
3516
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3517
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3518
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3519
			<div style="padding-left: 6ex;">
3520
				', $txt['upgrade_warning_lots_data'], '
3521
			</div>
3522
		</div>';
3523
3524
	// A warning message?
3525
	if (!empty($upcontext['warning']))
3526
		echo '
3527
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3528
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3529
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3530
			<div style="padding-left: 6ex;">
3531
				', $upcontext['warning'], '
3532
			</div>
3533
		</div>';
3534
3535
	// Paths are incorrect?
3536
	echo '
3537
		<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">
3538
			<div style="float: left; width: 2ex; font-size: 2em; color: black;">!!</div>
3539
			<strong style="text-decoration: underline;">', $txt['upgrade_critical_error'], '</strong><br>
3540
			<div style="padding-left: 6ex;">
3541
				', $txt['upgrade_error_script_js'], '
3542
			</div>
3543
		</div>';
3544
3545
	// Is there someone already doing this?
3546
	if (!empty($upcontext['user']['id']) && (time() - $upcontext['started'] < 72600 || time() - $upcontext['updated'] < 3600))
3547
	{
3548
		$ago = time() - $upcontext['started'];
3549
		if ($ago < 60)
3550
			$ago = $ago . ' seconds';
3551
		elseif ($ago < 3600)
3552
			$ago = (int) ($ago / 60) . ' minutes';
3553
		else
3554
			$ago = (int) ($ago / 3600) . ' hours';
3555
3556
		$active = time() - $upcontext['updated'];
3557
		if ($active < 60)
3558
			$updated = $active . ' seconds';
3559
		elseif ($active < 3600)
3560
			$updated = (int) ($active / 60) . ' minutes';
3561
		else
3562
			$updated = (int) ($active / 3600) . ' hours';
3563
3564
		echo '
3565
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3566
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3567
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3568
			<div style="padding-left: 6ex;">
3569
				&quot;', $upcontext['user']['name'], '&quot; has been running the upgrade script for the last ', $ago, ' - and was last active ', $updated, ' ago.';
3570
3571
		if ($active < 600)
3572
			echo '
3573
				We recommend that you do not run this script unless you are sure that ', $upcontext['user']['name'], ' has completed their upgrade.';
3574
3575
		if ($active > $upcontext['inactive_timeout'])
3576
			echo '
3577
				<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.';
3578
		else
3579
			echo '
3580
				<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!');
3581
3582
		echo '
3583
			</div>
3584
		</div>';
3585
	}
3586
3587
	echo '
3588
			<strong>Admin Login: ', $disable_security ? '(DISABLED)' : '', '</strong>
3589
			<h3>For security purposes please login with your admin account to proceed with the upgrade.</h3>
3590
			<table>
3591
				<tr valign="top">
3592
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Username:</strong></td>
3593
					<td>
3594
						<input type="text" name="user" value="', !empty($upcontext['username']) ? $upcontext['username'] : '', '"', $disable_security ? ' disabled' : '', ' class="input_text">';
3595
3596
	if (!empty($upcontext['username_incorrect']))
3597
		echo '
3598
						<div class="smalltext" style="color: red;">Username Incorrect</div>';
3599
3600
	echo '
3601
					</td>
3602
				</tr>
3603
				<tr valign="top">
3604
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Password:</strong></td>
3605
					<td>
3606
						<input type="password" name="passwrd" value=""', $disable_security ? ' disabled' : '', ' class="input_password">
3607
						<input type="hidden" name="hash_passwrd" value="">';
3608
3609
	if (!empty($upcontext['password_failed']))
3610
		echo '
3611
						<div class="smalltext" style="color: red;">Password Incorrect</div>';
3612
3613
	echo '
3614
					</td>
3615
				</tr>';
3616
3617
	// Can they continue?
3618
	if (!empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] >= $upcontext['inactive_timeout'] && $upcontext['user']['step'] > 1)
3619
	{
3620
		echo '
3621
				<tr>
3622
					<td colspan="2">
3623
						<label for="cont"><input type="checkbox" id="cont" name="cont" checked class="input_check">Continue from step reached during last execution of upgrade script.</label>
3624
					</td>
3625
				</tr>';
3626
	}
3627
3628
	echo '
3629
			</table><br>
3630
			<span class="smalltext">
3631
				<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.
3632
			</span>
3633
			<input type="hidden" name="login_attempt" id="login_attempt" value="1">
3634
			<input type="hidden" name="js_works" id="js_works" value="0">';
3635
3636
	// Say we want the continue button!
3637
	$upcontext['continue'] = !empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] < $upcontext['inactive_timeout'] ? 2 : 1;
3638
3639
	// This defines whether javascript is going to work elsewhere :D
3640
	echo '
3641
		<script>
3642
			if (\'XMLHttpRequest\' in window && document.getElementById(\'js_works\'))
3643
				document.getElementById(\'js_works\').value = 1;
3644
3645
			// Latest version?
3646
			function smfCurrentVersion()
3647
			{
3648
				var smfVer, yourVer;
3649
3650
				if (!(\'smfVersion\' in window))
3651
					return;
3652
3653
				window.smfVersion = window.smfVersion.replace(/SMF\s?/g, \'\');
3654
3655
				smfVer = document.getElementById(\'smfVersion\');
3656
				yourVer = document.getElementById(\'yourVersion\');
3657
3658
				setInnerHTML(smfVer, window.smfVersion);
3659
3660
				var currentVersion = getInnerHTML(yourVer);
3661
				if (currentVersion < window.smfVersion)
3662
					document.getElementById(\'version_warning\').style.display = \'\';
3663
			}
3664
			addLoadEvent(smfCurrentVersion);
3665
3666
			// This checks that the script file even exists!
3667
			if (typeof(smfSelectText) == \'undefined\')
3668
				document.getElementById(\'js_script_missing_error\').style.display = \'\';
3669
3670
		</script>';
3671
}
3672
3673
function template_upgrade_options()
3674
{
3675
	global $upcontext, $modSettings, $db_prefix, $mmessage, $mtitle, $db_type;
3676
3677
	echo '
3678
			<h3>Before the upgrade gets underway please review the options below - and hit continue when you\'re ready to begin.</h3>
3679
			<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">';
3680
3681
	// Warning message?
3682
	if (!empty($upcontext['upgrade_options_warning']))
3683
		echo '
3684
		<div style="margin: 1ex; padding: 1ex; border: 1px dashed #cc3344; color: black; background-color: #ffe4e9;">
3685
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3686
			<strong style="text-decoration: underline;">Warning!</strong><br>
3687
			<div style="padding-left: 4ex;">
3688
				', $upcontext['upgrade_options_warning'], '
3689
			</div>
3690
		</div>';
3691
3692
	echo '
3693
				<table>
3694
					<tr valign="top">
3695
						<td width="2%">
3696
							<input type="checkbox" name="backup" id="backup" value="1" class="input_check">
3697
						</td>
3698
						<td width="100%">
3699
							<label for="backup">Backup tables in your database with the prefix &quot;backup_' . $db_prefix . '&quot;.</label> (recommended!)
3700
						</td>
3701
					</tr>
3702
					<tr valign="top">
3703
						<td width="2%">
3704
							<input type="checkbox" name="maint" id="maint" value="1" checked class="input_check">
3705
						</td>
3706
						<td width="100%">
3707
							<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>
3708
							<div id="mainmess" style="display: none;">
3709
								<strong class="smalltext">Maintenance Title: </strong><br>
3710
								<input type="text" name="maintitle" size="30" value="', htmlspecialchars($mtitle), '" class="input_text"><br>
3711
								<strong class="smalltext">Maintenance Message: </strong><br>
3712
								<textarea name="mainmessage" rows="3" cols="50">', htmlspecialchars($mmessage), '</textarea>
3713
							</div>
3714
						</td>
3715
					</tr>
3716
					<tr valign="top">
3717
						<td width="2%">
3718
							<input type="checkbox" name="debug" id="debug" value="1" class="input_check">
3719
						</td>
3720
						<td width="100%">
3721
							<label for="debug">Output extra debugging information</label>
3722
						</td>
3723
					</tr>
3724
					<tr valign="top">
3725
						<td width="2%">
3726
							<input type="checkbox" name="empty_error" id="empty_error" value="1" class="input_check">
3727
						</td>
3728
						<td width="100%">
3729
							<label for="empty_error">Empty error log before upgrading</label>
3730
						</td>
3731
					</tr>';
3732
3733
	if (!empty($upcontext['karma_installed']['good']) || !empty($upcontext['karma_installed']['bad']))
3734
		echo '
3735
					<tr valign="top">
3736
						<td width="2%">
3737
							<input type="checkbox" name="delete_karma" id="delete_karma" value="1" class="input_check">
3738
						</td>
3739
						<td width="100%">
3740
							<label for="delete_karma">Delete all karma settings and info from the DB</label>
3741
						</td>
3742
					</tr>';
3743
3744
	echo '
3745
					<tr valign="top">
3746
						<td width="2%">
3747
							<input type="checkbox" name="stat" id="stat" value="1"', empty($modSettings['allow_sm_stats']) ? '' : ' checked', ' class="input_check">
3748
						</td>
3749
						<td width="100%">
3750
							<label for="stat">
3751
								Allow Simple Machines to Collect Basic Stats Monthly.<br>
3752
								<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="http://www.simplemachines.org/about/stats.php" target="_blank">info page</a>.</span>
3753
							</label>
3754
						</td>
3755
					</tr>
3756
				</table>
3757
				<input type="hidden" name="upcont" value="1">';
3758
3759
	// We need a normal continue button here!
3760
	$upcontext['continue'] = 1;
3761
}
3762
3763
// Template for the database backup tool/
3764
function template_backup_database()
3765
{
3766
	global $upcontext, $support_js, $is_debug;
3767
3768
	echo '
3769
			<h3>Please wait while a backup is created. For large forums this may take some time!</h3>';
3770
3771
	echo '
3772
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
3773
			<input type="hidden" name="backup_done" id="backup_done" value="0">
3774
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
3775
			<div id="debug_section" style="height: 200px; overflow: auto;">
3776
			<span id="debuginfo"></span>
3777
			</div>';
3778
3779
	// Dont any tables so far?
3780
	if (!empty($upcontext['previous_tables']))
3781
		foreach ($upcontext['previous_tables'] as $table)
3782
			echo '
3783
			<br>Completed Table: &quot;', $table, '&quot;.';
3784
3785
	echo '
3786
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
3787
			<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>';
3788
3789
	// Continue please!
3790
	$upcontext['continue'] = $support_js ? 2 : 1;
3791
3792
	// If javascript allows we want to do this using XML.
3793
	if ($support_js)
3794
	{
3795
		echo '
3796
		<script>
3797
			var lastTable = ', $upcontext['cur_table_num'], ';
3798
			function getNextTables()
3799
			{
3800
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
3801
			}
3802
3803
			// Got an update!
3804
			function onBackupUpdate(oXMLDoc)
3805
			{
3806
				var sCurrentTableName = "";
3807
				var iTableNum = 0;
3808
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
3809
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
3810
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
3811
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
3812
3813
				// Update the page.
3814
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
3815
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
3816
				lastTable = iTableNum;
3817
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
3818
3819
		// If debug flood the screen.
3820
		if ($is_debug)
3821
			echo '
3822
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
3823
3824
				if (document.getElementById(\'debug_section\').scrollHeight)
3825
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
3826
3827
		echo '
3828
				// Get the next update...
3829
				if (iTableNum == ', $upcontext['table_count'], ')
3830
				{
3831
					document.getElementById(\'commess\').style.display = "";
3832
					document.getElementById(\'current_tab_div\').style.display = "none";
3833
					document.getElementById(\'contbutt\').disabled = 0;
3834
					document.getElementById(\'backup_done\').value = 1;
3835
				}
3836
				else
3837
					getNextTables();
3838
			}
3839
			getNextTables();
3840
		</script>';
3841
	}
3842
}
3843
3844
function template_backup_xml()
3845
{
3846
	global $upcontext;
3847
3848
	echo '
3849
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
3850
}
3851
3852
// Here is the actual "make the changes" template!
3853
function template_database_changes()
3854
{
3855
	global $upcontext, $support_js, $is_debug, $timeLimitThreshold;
3856
3857
	if (empty($is_debug) && !empty($upcontext['upgrade_status']['debug']))
3858
		$is_debug = true;
3859
3860
	echo '
3861
		<h3>Executing database changes</h3>
3862
		<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>';
3863
3864
	echo '
3865
		<form action="', $upcontext['form_url'], '&amp;filecount=', $upcontext['file_count'], '" name="upform" id="upform" method="post">
3866
		<input type="hidden" name="database_done" id="database_done" value="0">';
3867
3868
	// No javascript looks rubbish!
3869
	if (!$support_js)
3870
	{
3871
		foreach ($upcontext['actioned_items'] as $num => $item)
3872
		{
3873
			if ($num != 0)
3874
				echo ' Successful!';
3875
			echo '<br>' . $item;
3876
		}
3877 View Code Duplication
		if (!empty($upcontext['changes_complete']))
3878
		{
3879
			if ($is_debug)
3880
			{
3881
				$active = time() - $upcontext['started'];
3882
				$hours = floor($active / 3600);
3883
				$minutes = intval(($active / 60) % 60);
3884
				$seconds = intval($active % 60);
3885
3886
				$totalTime = '';
3887
				if ($hours > 0)
3888
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
3889
				if ($minutes > 0)
3890
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
3891
				if ($seconds > 0)
3892
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
3893
			}
3894
3895
			if ($is_debug && !empty($totalTime))
3896
				echo ' Successful! Completed in ', $totalTime, '<br><br>';
3897
			else
3898
				echo ' Successful!<br><br>';
3899
3900
			echo '<span id="commess" style="font-weight: bold;">1 Database Updates Complete! Click Continue to Proceed.</span><br>';
3901
		}
3902
	}
3903
	else
3904
	{
3905
		// Tell them how many files we have in total.
3906
		if ($upcontext['file_count'] > 1)
3907
			echo '
3908
		<strong id="info1">Executing upgrade script <span id="file_done">', $upcontext['cur_file_num'], '</span> of ', $upcontext['file_count'], '.</strong>';
3909
3910
		echo '
3911
		<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>
3912
		<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>';
3913
3914 View Code Duplication
		if ($is_debug)
3915
		{
3916
			if ($upcontext['current_debug_item_num'] == $upcontext['debug_items'])
3917
			{
3918
				$active = time() - $upcontext['started'];
3919
				$hours = floor($active / 3600);
3920
				$minutes = intval(($active / 60) % 60);
3921
				$seconds = intval($active % 60);
3922
3923
				$totalTime = '';
3924
				if ($hours > 0)
3925
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
3926
				if ($minutes > 0)
3927
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
3928
				if ($seconds > 0)
3929
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
3930
			}
3931
3932
			echo '
3933
			<br><span id="upgradeCompleted">';
3934
3935
			if (!empty($totalTime))
3936
				echo 'Completed in ', $totalTime, '<br>';
3937
3938
			echo '</span>
3939
			<div id="debug_section" style="height: 200px; overflow: auto;">
3940
			<span id="debuginfo"></span>
3941
			</div>';
3942
		}
3943
	}
3944
3945
	// Place for the XML error message.
3946
	echo '
3947
		<div id="error_block" style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9; display: ', empty($upcontext['error_message']) ? 'none' : '', ';">
3948
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3949
			<strong style="text-decoration: underline;">Error!</strong><br>
3950
			<div style="padding-left: 6ex;" id="error_message">', isset($upcontext['error_message']) ? $upcontext['error_message'] : 'Unknown Error!', '</div>
3951
		</div>';
3952
3953
	// We want to continue at some point!
3954
	$upcontext['continue'] = $support_js ? 2 : 1;
3955
3956
	// If javascript allows we want to do this using XML.
3957
	if ($support_js)
3958
	{
3959
		echo '
3960
		<script>
3961
			var lastItem = ', $upcontext['current_debug_item_num'], ';
3962
			var sLastString = "', strtr($upcontext['current_debug_item_name'], array('"' => '&quot;')), '";
3963
			var iLastSubStepProgress = -1;
3964
			var curFile = ', $upcontext['cur_file_num'], ';
3965
			var totalItems = 0;
3966
			var prevFile = 0;
3967
			var retryCount = 0;
3968
			var testvar = 0;
3969
			var timeOutID = 0;
3970
			var getData = "";
3971
			var debugItems = ', $upcontext['debug_items'], ';';
3972
3973
		if ($is_debug)
3974
			echo '
3975
			var upgradeStartTime = ' . $upcontext['started'] . ';';
3976
3977
		echo '
3978
			function getNextItem()
3979
			{
3980
				// We want to track this...
3981
				if (timeOutID)
3982
					clearTimeout(timeOutID);
3983
				timeOutID = window.setTimeout("retTimeout()", ', (10 * $timeLimitThreshold), '000);
3984
3985
				getXMLDocument(\'', $upcontext['form_url'], '&xml&filecount=', $upcontext['file_count'], '&substep=\' + lastItem + getData, onItemUpdate);
3986
			}
3987
3988
			// Got an update!
3989
			function onItemUpdate(oXMLDoc)
3990
			{
3991
				var sItemName = "";
3992
				var sDebugName = "";
3993
				var iItemNum = 0;
3994
				var iSubStepProgress = -1;
3995
				var iDebugNum = 0;
3996
				var bIsComplete = 0;
3997
				getData = "";
3998
3999
				// We\'ve got something - so reset the timeout!
4000
				if (timeOutID)
4001
					clearTimeout(timeOutID);
4002
4003
				// Assume no error at this time...
4004
				document.getElementById("error_block").style.display = "none";
4005
4006
				// Are we getting some duff info?
4007
				if (!oXMLDoc.getElementsByTagName("item")[0])
4008
				{
4009
					// Too many errors?
4010
					if (retryCount > 15)
4011
					{
4012
						document.getElementById("error_block").style.display = "";
4013
						setInnerHTML(document.getElementById("error_message"), "Error retrieving information on step: " + (sDebugName == "" ? sLastString : sDebugName));';
4014
4015
	if ($is_debug)
4016
		echo '
4017
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4018
4019
	echo '
4020
					}
4021
					else
4022
					{
4023
						retryCount++;
4024
						getNextItem();
4025
					}
4026
					return false;
4027
				}
4028
4029
				// Never allow loops.
4030
				if (curFile == prevFile)
4031
				{
4032
					retryCount++;
4033
					if (retryCount > 10)
4034
					{
4035
						document.getElementById("error_block").style.display = "";
4036
						setInnerHTML(document.getElementById("error_message"), "Upgrade script appears to be going into a loop - step: " + sDebugName);';
4037
4038
	if ($is_debug)
4039
		echo '
4040
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4041
4042
	echo '
4043
					}
4044
				}
4045
				retryCount = 0;
4046
4047
				for (var i = 0; i < oXMLDoc.getElementsByTagName("item")[0].childNodes.length; i++)
4048
					sItemName += oXMLDoc.getElementsByTagName("item")[0].childNodes[i].nodeValue;
4049
				for (var i = 0; i < oXMLDoc.getElementsByTagName("debug")[0].childNodes.length; i++)
4050
					sDebugName += oXMLDoc.getElementsByTagName("debug")[0].childNodes[i].nodeValue;
4051
				for (var i = 0; i < oXMLDoc.getElementsByTagName("get").length; i++)
4052
				{
4053
					getData += "&" + oXMLDoc.getElementsByTagName("get")[i].getAttribute("key") + "=";
4054
					for (var j = 0; j < oXMLDoc.getElementsByTagName("get")[i].childNodes.length; j++)
4055
					{
4056
						getData += oXMLDoc.getElementsByTagName("get")[i].childNodes[j].nodeValue;
4057
					}
4058
				}
4059
4060
				iItemNum = oXMLDoc.getElementsByTagName("item")[0].getAttribute("num");
4061
				iDebugNum = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("num"));
4062
				bIsComplete = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("complete"));
4063
				iSubStepProgress = parseFloat(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("percent"));
4064
				sLastString = sDebugName + " (Item: " + iDebugNum + ")";
4065
4066
				curFile = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("num"));
4067
				debugItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("debug_items"));
4068
				totalItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("items"));
4069
4070
				// If we have an error we haven\'t completed!
4071
				if (oXMLDoc.getElementsByTagName("error")[0] && bIsComplete)
4072
					iDebugNum = lastItem;
4073
4074
				// Do we have the additional progress bar?
4075
				if (iSubStepProgress != -1)
4076
				{
4077
					document.getElementById("substep_bar_div").style.display = "";
4078
					document.getElementById("substep_bar_div2").style.display = "";
4079
					document.getElementById("substep_progress").style.width = iSubStepProgress + "%";
4080
					setInnerHTML(document.getElementById("substep_text"), iSubStepProgress + "%");
4081
					setInnerHTML(document.getElementById("substep_bar_div"), sDebugName.replace(/\./g, "") + ":");
4082
				}
4083
				else
4084
				{
4085
					document.getElementById("substep_bar_div").style.display = "none";
4086
					document.getElementById("substep_bar_div2").style.display = "none";
4087
				}
4088
4089
				// Move onto the next item?
4090
				if (bIsComplete)
4091
					lastItem = iDebugNum;
4092
				else
4093
					lastItem = iDebugNum - 1;
4094
4095
				// Are we finished?
4096
				if (bIsComplete && iDebugNum == -1 && curFile >= ', $upcontext['file_count'], ')
4097
				{';
4098
4099
		if ($is_debug)
4100
			echo '
4101
					document.getElementById(\'debug_section\').style.display = "none";
4102
4103
					var upgradeFinishedTime = parseInt(oXMLDoc.getElementsByTagName("curtime")[0].childNodes[0].nodeValue);
4104
					var diffTime = upgradeFinishedTime - upgradeStartTime;
4105
					var diffHours = Math.floor(diffTime / 3600);
4106
					var diffMinutes = parseInt((diffTime / 60) % 60);
4107
					var diffSeconds = parseInt(diffTime % 60);
4108
4109
					var totalTime = "";
4110
					if (diffHours > 0)
4111
						totalTime = totalTime + diffHours + " hour" + (diffHours > 1 ? "s" : "") + " ";
4112
					if (diffMinutes > 0)
4113
						totalTime = totalTime + diffMinutes + " minute" + (diffMinutes > 1 ? "s" : "") + " ";
4114
					if (diffSeconds > 0)
4115
						totalTime = totalTime + diffSeconds + " second" + (diffSeconds > 1 ? "s" : "");
4116
4117
					setInnerHTML(document.getElementById("upgradeCompleted"), "Completed in " + totalTime);';
4118
4119
		echo '
4120
4121
					document.getElementById(\'commess\').style.display = "";
4122
					document.getElementById(\'contbutt\').disabled = 0;
4123
					document.getElementById(\'database_done\').value = 1;';
4124
4125
		if ($upcontext['file_count'] > 1)
4126
			echo '
4127
					document.getElementById(\'info1\').style.display = "none";';
4128
4129
		echo '
4130
					document.getElementById(\'info2\').style.display = "none";
4131
					updateStepProgress(100, 100, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4132
					return true;
4133
				}
4134
				// Was it the last step in the file?
4135
				else if (bIsComplete && iDebugNum == -1)
4136
				{
4137
					lastItem = 0;
4138
					prevFile = curFile;';
4139
4140
		if ($is_debug)
4141
			echo '
4142
					setOuterHTML(document.getElementById(\'debuginfo\'), \'Moving to next script file...done<br><span id="debuginfo"><\' + \'/span>\');';
4143
4144
		echo '
4145
					getNextItem();
4146
					return true;
4147
				}';
4148
4149
		// If debug scroll the screen.
4150
		if ($is_debug)
4151
			echo '
4152
				if (iLastSubStepProgress == -1)
4153
				{
4154
					// Give it consistent dots.
4155
					dots = sDebugName.match(/\./g);
4156
					numDots = dots ? dots.length : 0;
4157
					for (var i = numDots; i < 3; i++)
4158
						sDebugName += ".";
4159
					setOuterHTML(document.getElementById(\'debuginfo\'), sDebugName + \'<span id="debuginfo"><\' + \'/span>\');
4160
				}
4161
				iLastSubStepProgress = iSubStepProgress;
4162
4163
				if (bIsComplete)
4164
					setOuterHTML(document.getElementById(\'debuginfo\'), \'done<br><span id="debuginfo"><\' + \'/span>\');
4165
				else
4166
					setOuterHTML(document.getElementById(\'debuginfo\'), \'...<span id="debuginfo"><\' + \'/span>\');
4167
4168
				if (document.getElementById(\'debug_section\').scrollHeight)
4169
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4170
4171
		echo '
4172
				// Update the page.
4173
				setInnerHTML(document.getElementById(\'item_num\'), iItemNum);
4174
				setInnerHTML(document.getElementById(\'cur_item_name\'), sItemName);';
4175
4176
		if ($upcontext['file_count'] > 1)
4177
		{
4178
			echo '
4179
				setInnerHTML(document.getElementById(\'file_done\'), curFile);
4180
				setInnerHTML(document.getElementById(\'item_count\'), totalItems);';
4181
		}
4182
4183
		echo '
4184
				// Is there an error?
4185
				if (oXMLDoc.getElementsByTagName("error")[0])
4186
				{
4187
					var sErrorMsg = "";
4188
					for (var i = 0; i < oXMLDoc.getElementsByTagName("error")[0].childNodes.length; i++)
4189
						sErrorMsg += oXMLDoc.getElementsByTagName("error")[0].childNodes[i].nodeValue;
4190
					document.getElementById("error_block").style.display = "";
4191
					setInnerHTML(document.getElementById("error_message"), sErrorMsg);
4192
					return false;
4193
				}
4194
4195
				// Get the progress bar right.
4196
				barTotal = debugItems * ', $upcontext['file_count'], ';
4197
				barDone = (debugItems * (curFile - 1)) + lastItem;
4198
4199
				updateStepProgress(barDone, barTotal, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4200
4201
				// Finally - update the time here as it shows the server is responding!
4202
				curTime = new Date();
4203
				iElapsed = (curTime.getTime() / 1000 - ', $upcontext['started'], ');
4204
				mins = parseInt(iElapsed / 60);
4205
				secs = parseInt(iElapsed - mins * 60);
4206
				setInnerHTML(document.getElementById("mins_elapsed"), mins);
4207
				setInnerHTML(document.getElementById("secs_elapsed"), secs);
4208
4209
				getNextItem();
4210
				return true;
4211
			}
4212
4213
			// What if we timeout?!
4214
			function retTimeout(attemptAgain)
4215
			{
4216
				// Oh noes...
4217
				if (!attemptAgain)
4218
				{
4219
					document.getElementById("error_block").style.display = "";
4220
					setInnerHTML(document.getElementById("error_message"), "Server has not responded for ', ($timeLimitThreshold * 10), ' 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");
4221
				}
4222
				else
4223
				{
4224
					document.getElementById("error_block").style.display = "none";
4225
					getNextItem();
4226
				}
4227
			}';
4228
4229
		// Start things off assuming we've not errored.
4230
		if (empty($upcontext['error_message']))
4231
			echo '
4232
			getNextItem();';
4233
4234
		echo '
4235
		</script>';
4236
	}
4237
	return;
4238
}
4239
4240
function template_database_xml()
4241
{
4242
	global $is_debug, $upcontext;
4243
4244
	echo '
4245
	<file num="', $upcontext['cur_file_num'], '" items="', $upcontext['total_items'], '" debug_items="', $upcontext['debug_items'], '">', $upcontext['cur_file_name'], '</file>
4246
	<item num="', $upcontext['current_item_num'], '">', $upcontext['current_item_name'], '</item>
4247
	<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>';
4248
4249
	if (!empty($upcontext['error_message']))
4250
		echo '
4251
	<error>', $upcontext['error_message'], '</error>';
4252
4253
	if (!empty($upcontext['error_string']))
4254
		echo '
4255
	<sql>', $upcontext['error_string'], '</sql>';
4256
4257
	if ($is_debug)
4258
		echo '
4259
	<curtime>', time(), '</curtime>';
4260
}
4261
4262
// Template for the UTF-8 conversion step. Basically a copy of the backup stuff with slight modifications....
4263 View Code Duplication
function template_convert_utf8()
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

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

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

Loading history...
4264
{
4265
	global $upcontext, $support_js, $is_debug;
4266
4267
	echo '
4268
			<h3>Please wait while your database is converted to UTF-8. For large forums this may take some time!</h3>';
4269
4270
	echo '
4271
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4272
			<input type="hidden" name="utf8_done" id="utf8_done" value="0">
4273
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
4274
			<span id="debuginfo"></span>';
4275
4276
	// Done any tables so far?
4277
	if (!empty($upcontext['previous_tables']))
4278
		foreach ($upcontext['previous_tables'] as $table)
4279
			echo '
4280
			<br>Completed Table: &quot;', $table, '&quot;.';
4281
4282
	echo '
4283
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>';
4284
4285
	// If we dropped their index, let's let them know
4286
	if ($upcontext['cur_table_num'] == $upcontext['table_count'] && $upcontext['dropping_index'])
4287
		echo '
4288
			<br><span style="display:inline;">Please note that your fulltext index was dropped to facilitate the conversion and will need to be recreated.</span>';
4289
4290
	echo '
4291
			<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>';
4292
4293
	// Continue please!
4294
	$upcontext['continue'] = $support_js ? 2 : 1;
4295
4296
	// If javascript allows we want to do this using XML.
4297
	if ($support_js)
4298
	{
4299
		echo '
4300
		<script>
4301
			var lastTable = ', $upcontext['cur_table_num'], ';
4302
			function getNextTables()
4303
			{
4304
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4305
			}
4306
4307
			// Got an update!
4308
			function onBackupUpdate(oXMLDoc)
4309
			{
4310
				var sCurrentTableName = "";
4311
				var iTableNum = 0;
4312
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4313
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4314
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4315
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4316
4317
				// Update the page.
4318
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4319
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4320
				lastTable = iTableNum;
4321
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4322
4323
		// If debug flood the screen.
4324
		if ($is_debug)
4325
			echo '
4326
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');';
4327
4328
		echo '
4329
				// Get the next update...
4330
				if (iTableNum == ', $upcontext['table_count'], ')
4331
				{
4332
					document.getElementById(\'commess\').style.display = "";
4333
					document.getElementById(\'current_tab_div\').style.display = "none";
4334
					document.getElementById(\'contbutt\').disabled = 0;
4335
					document.getElementById(\'utf8_done\').value = 1;
4336
				}
4337
				else
4338
					getNextTables();
4339
			}
4340
			getNextTables();
4341
		</script>';
4342
	}
4343
}
4344
4345
function template_utf8_xml()
4346
{
4347
	global $upcontext;
4348
4349
	echo '
4350
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4351
}
4352
4353
// Template for the database backup tool/
4354 View Code Duplication
function template_serialize_json()
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

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

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

Loading history...
4355
{
4356
	global $upcontext, $support_js, $is_debug;
4357
4358
	echo '
4359
			<h3>Converting data from serialize to JSON...</h3>';
4360
4361
	echo '
4362
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4363
			<input type="hidden" name="json_done" id="json_done" value="0">
4364
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
4365
			<span id="debuginfo"></span>';
4366
4367
	// Dont any tables so far?
4368
	if (!empty($upcontext['previous_tables']))
4369
		foreach ($upcontext['previous_tables'] as $table)
4370
			echo '
4371
			<br>Completed Table: &quot;', $table, '&quot;.';
4372
4373
	echo '
4374
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
4375
			<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>';
4376
4377
	// Try to make sure substep was reset.
4378
	if ($upcontext['cur_table_num'] == $upcontext['table_count'])
4379
		echo '
4380
			<input type="hidden" name="substep" id="substep" value="0">';
4381
4382
	// Continue please!
4383
	$upcontext['continue'] = $support_js ? 2 : 1;
4384
4385
	// If javascript allows we want to do this using XML.
4386
	if ($support_js)
4387
	{
4388
		echo '
4389
		<script>
4390
			var lastTable = ', $upcontext['cur_table_num'], ';
4391
			function getNextTables()
4392
			{
4393
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4394
			}
4395
4396
			// Got an update!
4397
			function onBackupUpdate(oXMLDoc)
4398
			{
4399
				var sCurrentTableName = "";
4400
				var iTableNum = 0;
4401
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4402
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4403
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4404
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4405
4406
				// Update the page.
4407
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4408
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4409
				lastTable = iTableNum;
4410
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4411
4412
		// If debug flood the screen.
4413
		if ($is_debug)
4414
			echo '
4415
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');';
4416
4417
		echo '
4418
				// Get the next update...
4419
				if (iTableNum == ', $upcontext['table_count'], ')
4420
				{
4421
					document.getElementById(\'commess\').style.display = "";
4422
					document.getElementById(\'current_tab_div\').style.display = "none";
4423
					document.getElementById(\'contbutt\').disabled = 0;
4424
					document.getElementById(\'json_done\').value = 1;
4425
				}
4426
				else
4427
					getNextTables();
4428
			}
4429
			getNextTables();
4430
		</script>';
4431
	}
4432
}
4433
4434
function template_serialize_json_xml()
4435
{
4436
	global $upcontext;
4437
4438
	echo '
4439
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4440
}
4441
4442
function template_upgrade_complete()
4443
{
4444
	global $upcontext, $upgradeurl, $settings, $boardurl, $is_debug;
4445
4446
	echo '
4447
	<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>
4448
	<form action="', $boardurl, '/index.php">';
4449
4450
	if (!empty($upcontext['can_delete_script']))
4451
		echo '
4452
			<label for="delete_self"><input type="checkbox" id="delete_self" onclick="doTheDelete(this);" class="input_check"> Delete upgrade.php and its data files now</label> <em>(doesn\'t work on all servers).</em>
4453
			<script>
4454
				function doTheDelete(theCheck)
4455
				{
4456
					var theImage = document.getElementById ? document.getElementById("delete_upgrader") : document.all.delete_upgrader;
4457
4458
					theImage.src = "', $upgradeurl, '?delete=1&ts_" + (new Date().getTime());
4459
					theCheck.disabled = true;
4460
				}
4461
			</script>
4462
			<img src="', $settings['default_theme_url'], '/images/blank.png" alt="" id="delete_upgrader"><br>';
4463
4464
	$active = time() - $upcontext['started'];
4465
	$hours = floor($active / 3600);
4466
	$minutes = intval(($active / 60) % 60);
4467
	$seconds = intval($active % 60);
4468
4469
	if ($is_debug)
4470
	{
4471
		$totalTime = '';
4472
		if ($hours > 0)
4473
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
4474
		if ($minutes > 0)
4475
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
4476
		if ($seconds > 0)
4477
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
4478
	}
4479
4480
	if ($is_debug && !empty($totalTime))
4481
		echo '<br> Upgrade completed in ', $totalTime, '<br><br>';
4482
4483
	echo '<br>
4484
			If you had any problems with this upgrade, or have any problems using SMF, please don\'t hesitate to <a href="http://www.simplemachines.org/community/index.php">look to us for assistance</a>.<br>
4485
			<br>
4486
			Best of luck,<br>
4487
			Simple Machines';
4488
}
4489
4490
/**
4491
 * Convert MySQL (var)char ip col to binary
4492
 *
4493
 * @param string $targetTable The table to perform the operation on
4494
 * @param string $oldCol The old column to gather data from
4495
 * @param string $newCol The new column to put data in
4496
 * @param int $limit The amount of entries to handle at once.
4497
 * @param int $setSize The amount of entries after which to update the database.
4498
 *
4499
 * newCol needs to be a varbinary(16) null able field
4500
 * @return bool
4501
 */
4502
function MySQLConvertOldIp($targetTable, $oldCol, $newCol, $limit = 50000, $setSize = 100)
4503
{
4504
	global $smcFunc, $step_progress;
4505
4506
	$current_substep = $_GET['substep'];
4507
4508
	$step_progress['name'] = 'Converting ips';
4509
	$step_progress['current'] = !empty($_GET['a']) ? $_GET['a'] : 0;
4510
4511
	// Skip this if we don't have the column
4512
	$request = $smcFunc['db_query']('', '
4513
		SHOW FIELDS
4514
		FROM {db_prefix}{raw:table}
4515
		WHERE Field = {string:name}',
4516
		array(
4517
			'table' => $targetTable,
4518
			'name' => $oldCol,
4519
	));
4520
	if ($smcFunc['db_num_rows']($request) !== 1)
4521
	{
4522
		$smcFunc['db_free_result']($request);
4523
		return;
4524
	}
4525
	$smcFunc['db_free_result']($request);
4526
4527
	//mysql default max length is 1mb http://dev.mysql.com/doc/refman/5.1/en/packet-too-large.html
4528
	$arIp = array();
4529
4530
	$is_done = false;
4531
	while (!$is_done)
4532
	{
4533
		// Keep looping at the current step.
4534
		nextSubStep($current_substep);
4535
4536
		$request = $smcFunc['db_query']('', '
4537
			SELECT DISTINCT {raw:old_col}
4538
			FROM {db_prefix}{raw:table_name}
4539
			WHERE {raw:new_col} IS NULL
4540
			LIMIT {int:limit}',
4541
			array(
4542
				'old_col' => $oldCol,
4543
				'new_col' => $newCol,
4544
				'table_name' => $targetTable,
4545
				'empty' => '',
4546
				'limit' => $limit,
4547
		));
4548
		while ($row = $smcFunc['db_fetch_assoc']($request))
4549
			$arIp[] = $row[$oldCol];
4550
		$smcFunc['db_free_result']($request);
4551
4552
		// Special case, null ip could keep us in a loop.
4553
		if (is_null($arIp[0]))
4554
			unset($arIp[0]);
4555
4556
		if (empty($arIp))
4557
			$is_done = true;
4558
4559
		$updates = array();
4560
		$cases = array();
4561
		$count = count($arIp);
4562
		for ($i = 0; $i < $count; $i++)
4563
		{
4564
			$arIp[$i] = trim($arIp[$i]);
4565
4566
			if (empty($arIp[$i]))
4567
				continue;
4568
4569
			$updates['ip' . $i] = $arIp[$i];
4570
			$cases[$arIp[$i]] = 'WHEN ' . $oldCol . ' = {string:ip' . $i . '} THEN {inet:ip' . $i . '}';
4571
4572
			if ($setSize > 0 && $i % $setSize === 0)
4573
			{
4574
				if (count($updates) == 1)
4575
					continue;
4576
4577
				$updates['whereSet'] = array_values($updates);
4578
				$smcFunc['db_query']('', '
4579
					UPDATE {db_prefix}' . $targetTable . '
4580
					SET ' . $newCol . ' = CASE ' .
4581
					implode('
4582
						', $cases) . '
4583
						ELSE NULL
4584
					END
4585
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4586
					$updates
4587
				);
4588
4589
				$updates = array();
4590
				$cases = array();
4591
			}
4592
		}
4593
4594
		// Incase some extras made it through.
4595
		if (!empty($updates))
4596
		{
4597
			if (count($updates) == 1)
4598
			{
4599
				foreach ($updates as $key => $ip)
4600
				{
4601
					$smcFunc['db_query']('', '
4602
						UPDATE {db_prefix}' . $targetTable . '
4603
						SET ' . $newCol . ' = {inet:ip}
4604
						WHERE ' . $oldCol . ' = {string:ip}',
4605
						array(
4606
							'ip' => $ip
4607
					));
4608
				}
4609
			}
4610
			else
4611
			{
4612
				$updates['whereSet'] = array_values($updates);
4613
				$smcFunc['db_query']('', '
4614
					UPDATE {db_prefix}' . $targetTable . '
4615
					SET ' . $newCol . ' = CASE ' .
4616
					implode('
4617
						', $cases) . '
4618
						ELSE NULL
4619
					END
4620
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4621
					$updates
4622
				);
4623
			}
4624
		}
4625
		else
4626
			$is_done = true;
4627
4628
		$_GET['a'] += $limit;
4629
		$step_progress['current'] = $_GET['a'];
4630
	}
4631
4632
	unset($_GET['a']);
4633
}
4634
4635
?>
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...
4636