Completed
Push — release-2.1 ( 2dc2a3...f7f069 )
by Michael
04:25 queued 04:17
created

upgrade.php ➔ upgradeGetColumnInfo()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 2
nop 2
dl 0
loc 14
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines http://www.simplemachines.org
8
 * @copyright 2017 Simple Machines and individual contributors
9
 * @license http://www.simplemachines.org/about/smf/license.php BSD
10
 *
11
 * @version 2.1 Beta 3
12
 */
13
14
// Version information...
15
define('SMF_VERSION', '2.1 Beta 3');
16
define('SMF_LANG_VERSION', '2.1 Beta 3');
17
18
/**
19
 * The minimum required PHP version.
20
 * @var string
21
 */
22
$GLOBALS['required_php_version'] = '5.3.8';
23
24
/**
25
 * A list of supported database systems.
26
 * @var array
27
 */
28
$databases = array(
29
	'mysql' => array(
30
		'name' => 'MySQL',
31
		'version' => '5.0.3',
32
		'version_check' => 'global $db_connection; return min(mysqli_get_server_info($db_connection), mysqli_get_client_info());',
33
		'utf8_support' => true,
34
		'utf8_version' => '5.0.3',
35
		'utf8_version_check' => 'global $db_connection; return mysqli_get_server_info($db_connection);',
36
		'alter_support' => true,
37
	),
38
	'postgresql' => array(
39
		'name' => 'PostgreSQL',
40
		'version' => '9.1',
41
		'version_check' => '$version = pg_version(); return $version[\'client\'];',
42
		'always_has_db' => true,
43
	),
44
);
45
46
/**
47
 * The maximum time a single substep may take, in seconds.
48
 * @var int
49
 */
50
$timeLimitThreshold = 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
// The helper is crucial. Include it first thing.
84
if (!file_exists($upgrade_path . '/upgrade-helper.php'))
85
    die('upgrade-helper.php not found where it was expected: ' . $upgrade_path . '/upgrade-helper.php! Make sure you have uploaded ALL files from the upgrade package. The upgrader cannot continue.');
86
87
require_once($upgrade_path . '/upgrade-helper.php');
88
89
// All the steps in detail.
90
// Number,Name,Function,Progress Weight.
91
$upcontext['steps'] = array(
92
	0 => array(1, 'Login', 'WelcomeLogin', 2),
93
	1 => array(2, 'Upgrade Options', 'UpgradeOptions', 2),
94
	2 => array(3, 'Backup', 'BackupDatabase', 10),
95
	3 => array(4, 'Database Changes', 'DatabaseChanges', 50),
96
	4 => array(5, 'Convert to UTF-8', 'ConvertUtf8', 20),
97
	5 => array(6, 'Convert serialized strings to JSON', 'serialize_to_json', 10),
98
	6 => array(7, 'Delete Upgrade.php', 'DeleteUpgrade', 1),
99
);
100
// Just to remember which one has files in it.
101
$upcontext['database_step'] = 3;
102
@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...
103
if (!ini_get('safe_mode'))
104
{
105
	ini_set('mysql.connect_timeout', -1);
106
	ini_set('default_socket_timeout', 900);
107
}
108
// Clean the upgrade path if this is from the client.
109
if (!empty($_SERVER['argv']) && php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
110
	for ($i = 1; $i < $_SERVER['argc']; $i++)
111
	{
112
		if (preg_match('~^--path=(.+)$~', $_SERVER['argv'][$i], $match) != 0)
113
			$upgrade_path = substr($match[1], -1) == '/' ? substr($match[1], 0, -1) : $match[1];
114
	}
115
116
// Are we from the client?
117
if (php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
118
{
119
	$command_line = true;
120
	$disable_security = true;
121
}
122
else
123
	$command_line = false;
124
125
// Load this now just because we can.
126
require_once($upgrade_path . '/Settings.php');
127
128
// Are we logged in?
129
if (isset($upgradeData))
130
{
131
	$upcontext['user'] = json_decode(base64_decode($upgradeData), true);
132
133
	// Check for sensible values.
134 View Code Duplication
	if (empty($upcontext['user']['started']) || $upcontext['user']['started'] < time() - 86400)
135
		$upcontext['user']['started'] = time();
136 View Code Duplication
	if (empty($upcontext['user']['updated']) || $upcontext['user']['updated'] < time() - 86400)
137
		$upcontext['user']['updated'] = 0;
138
139
	$upcontext['started'] = $upcontext['user']['started'];
140
	$upcontext['updated'] = $upcontext['user']['updated'];
141
142
	$is_debug = !empty($upcontext['user']['debug']) ? true : false;
143
}
144
145
// Nothing sensible?
146
if (empty($upcontext['updated']))
147
{
148
	$upcontext['started'] = time();
149
	$upcontext['updated'] = 0;
150
	$upcontext['user'] = array(
151
		'id' => 0,
152
		'name' => 'Guest',
153
		'pass' => 0,
154
		'started' => $upcontext['started'],
155
		'updated' => $upcontext['updated'],
156
	);
157
}
158
159
// Load up some essential data...
160
loadEssentialData();
161
162
// Are we going to be mimic'ing SSI at this point?
163
if (isset($_GET['ssi']))
164
{
165
	require_once($sourcedir . '/Errors.php');
166
	require_once($sourcedir . '/Logging.php');
167
	require_once($sourcedir . '/Load.php');
168
	require_once($sourcedir . '/Security.php');
169
	require_once($sourcedir . '/Subs-Package.php');
170
171
	loadUserSettings();
172
	loadPermissions();
173
}
174
175
// Include our helper functions.
176
require_once($sourcedir . '/Subs.php');
177
require_once($sourcedir . '/LogInOut.php');
178
179
// This only exists if we're on SMF ;)
180
if (isset($modSettings['smfVersion']))
181
{
182
	$request = $smcFunc['db_query']('', '
183
		SELECT variable, value
184
		FROM {db_prefix}themes
185
		WHERE id_theme = {int:id_theme}
186
			AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
187
		array(
188
			'id_theme' => 1,
189
			'theme_url' => 'theme_url',
190
			'theme_dir' => 'theme_dir',
191
			'images_url' => 'images_url',
192
			'db_error_skip' => true,
193
		)
194
	);
195 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
196
		$modSettings[$row['variable']] = $row['value'];
197
	$smcFunc['db_free_result']($request);
198
}
199
200
if (!isset($modSettings['theme_url']))
201
{
202
	$modSettings['theme_dir'] = $boarddir . '/Themes/default';
203
	$modSettings['theme_url'] = 'Themes/default';
204
	$modSettings['images_url'] = 'Themes/default/images';
205
}
206
if (!isset($settings['default_theme_url']))
207
	$settings['default_theme_url'] = $modSettings['theme_url'];
208
if (!isset($settings['default_theme_dir']))
209
	$settings['default_theme_dir'] = $modSettings['theme_dir'];
210
211
$upcontext['is_large_forum'] = (empty($modSettings['smfVersion']) || $modSettings['smfVersion'] <= '1.1 RC1') && !empty($modSettings['totalMessages']) && $modSettings['totalMessages'] > 75000;
212
// Default title...
213
$upcontext['page_title'] = 'Updating Your SMF Installation!';
214
215
// Have we got tracking data - if so use it (It will be clean!)
216
if (isset($_GET['data']))
217
{
218
	global $is_debug;
219
220
	$upcontext['upgrade_status'] = json_decode(base64_decode($_GET['data']), true);
221
	$upcontext['current_step'] = $upcontext['upgrade_status']['curstep'];
222
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
223
	$upcontext['rid'] = $upcontext['upgrade_status']['rid'];
224
	$support_js = $upcontext['upgrade_status']['js'];
225
226
	// Only set this if the upgrader status says so.
227
	if (empty($is_debug))
228
		$is_debug = $upcontext['upgrade_status']['debug'];
229
230
	// Load the language.
231
	if (file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
232
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
233
}
234
// Set the defaults.
235
else
236
{
237
	$upcontext['current_step'] = 0;
238
	$upcontext['rid'] = mt_rand(0, 5000);
239
	$upcontext['upgrade_status'] = array(
240
		'curstep' => 0,
241
		'lang' => isset($_GET['lang']) ? $_GET['lang'] : basename($language, '.lng'),
242
		'rid' => $upcontext['rid'],
243
		'pass' => 0,
244
		'debug' => 0,
245
		'js' => 0,
246
	);
247
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
248
}
249
250
// If this isn't the first stage see whether they are logging in and resuming.
251
if ($upcontext['current_step'] != 0 || !empty($upcontext['user']['step']))
252
	checkLogin();
253
254
if ($command_line)
255
	cmdStep0();
256
257
// Don't error if we're using xml.
258
if (isset($_GET['xml']))
259
	$upcontext['return_error'] = true;
260
261
// Loop through all the steps doing each one as required.
262
$upcontext['overall_percent'] = 0;
263
foreach ($upcontext['steps'] as $num => $step)
264
{
265
	if ($num >= $upcontext['current_step'])
266
	{
267
		// The current weight of this step in terms of overall progress.
268
		$upcontext['step_weight'] = $step[3];
269
		// Make sure we reset the skip button.
270
		$upcontext['skip'] = false;
271
272
		// We cannot proceed if we're not logged in.
273
		if ($num != 0 && !$disable_security && $upcontext['user']['pass'] != $upcontext['upgrade_status']['pass'])
274
		{
275
			$upcontext['steps'][0][2]();
276
			break;
277
		}
278
279
		// Call the step and if it returns false that means pause!
280 View Code Duplication
		if (function_exists($step[2]) && $step[2]() === false)
281
			break;
282
		elseif (function_exists($step[2]))
283
			$upcontext['current_step']++;
284
	}
285
	$upcontext['overall_percent'] += $step[3];
286
}
287
288
upgradeExit();
289
290
// Exit the upgrade script.
291
function upgradeExit($fallThrough = false)
292
{
293
	global $upcontext, $upgradeurl, $sourcedir, $command_line, $is_debug;
294
295
	// Save where we are...
296
	if (!empty($upcontext['current_step']) && !empty($upcontext['user']['id']))
297
	{
298
		$upcontext['user']['step'] = $upcontext['current_step'];
299
		$upcontext['user']['substep'] = $_GET['substep'];
300
		$upcontext['user']['updated'] = time();
301
		$upcontext['debug'] = $is_debug;
302
		$upgradeData = base64_encode(json_encode($upcontext['user']));
303
		require_once($sourcedir . '/Subs-Admin.php');
304
		updateSettingsFile(array('upgradeData' => '"' . $upgradeData . '"'));
305
		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...
306
	}
307
308
	// Handle the progress of the step, if any.
309
	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
310
	{
311
		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
312
		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
313
	}
314
	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
315
316
	// We usually dump our templates out.
317
	if (!$fallThrough)
318
	{
319
		// This should not happen my dear... HELP ME DEVELOPERS!!
320
		if (!empty($command_line))
321
		{
322
			if (function_exists('debug_print_backtrace'))
323
				debug_print_backtrace();
324
325
			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 299
  2. $upcontext is assigned
    in other/upgrade.php on line 300
  3. $upcontext is assigned
    in other/upgrade.php on line 301
  4. $upcontext is assigned
    in other/upgrade.php on line 314

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...
326
			flush();
327
			die();
328
		}
329
330
		if (!isset($_GET['xml']))
331
			template_upgrade_above();
332
		else
333
		{
334
			header('Content-Type: text/xml; charset=UTF-8');
335
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
336
			$upcontext['get_data'] = array();
337
			foreach ($_GET as $k => $v)
338
			{
339
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
340
				{
341
					$upcontext['get_data'][$k] = $v;
342
				}
343
			}
344
			template_xml_above();
345
		}
346
347
		// Call the template.
348
		if (isset($upcontext['sub_template']))
349
		{
350
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
351
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(json_encode($upcontext['upgrade_status']));
352
353
			// Custom stuff to pass back?
354
			if (!empty($upcontext['query_string']))
355
				$upcontext['form_url'] .= $upcontext['query_string'];
356
357
			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 299
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 299
  2. $upcontext is assigned
    in other/upgrade.php on line 300
  3. $upcontext is assigned
    in other/upgrade.php on line 301
  4. $upcontext is assigned
    in other/upgrade.php on line 314
  5. $upcontext is assigned
    in other/upgrade.php on line 350
  6. $upcontext is assigned
    in other/upgrade.php on line 351
  2. Path: Read from $_GET, and $v is assigned in other/upgrade.php on line 337
  1. Read from $_GET, and $v is assigned
    in other/upgrade.php on line 337
  2. $upcontext is assigned
    in other/upgrade.php on line 341
  3. $upcontext is assigned
    in other/upgrade.php on line 350
  4. $upcontext is assigned
    in other/upgrade.php on line 351
  3. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 351
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 351

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
360
		// Was there an error?
361
		if (!empty($upcontext['forced_error_message']))
362
			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 299
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 299
  2. $upcontext is assigned
    in other/upgrade.php on line 300
  3. $upcontext is assigned
    in other/upgrade.php on line 301
  4. $upcontext is assigned
    in other/upgrade.php on line 314
  2. Path: Read from $_GET, and $v is assigned in other/upgrade.php on line 337
  1. Read from $_GET, and $v is assigned
    in other/upgrade.php on line 337
  2. $upcontext is assigned
    in other/upgrade.php on line 341
  3. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 351
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 351

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...
363
364
		// Show the footer.
365
		if (!isset($_GET['xml']))
366
			template_upgrade_below();
367
		else
368
			template_xml_below();
369
	}
370
371
372
	if (!empty($command_line) && $is_debug)
373
	{
374
		$active = time() - $upcontext['started'];
375
		$hours = floor($active / 3600);
376
		$minutes = intval(($active / 60) % 60);
377
		$seconds = intval($active % 60);
378
379
		$totalTime = '';
380
		if ($hours > 0)
381
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
382
		if ($minutes > 0)
383
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
384
		if ($seconds > 0)
385
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
386
387
		if (!empty($totalTime))
388
			echo "\n" . 'Upgrade completed in ' . $totalTime . "\n";
389
	}
390
391
	// Bang - gone!
392
	die();
393
}
394
395
// Used to direct the user to another location.
396
function redirectLocation($location, $addForm = true)
397
{
398
	global $upgradeurl, $upcontext, $command_line;
399
400
	// Command line users can't be redirected.
401
	if ($command_line)
402
		upgradeExit(true);
403
404
	// Are we providing the core info?
405
	if ($addForm)
406
	{
407
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
408
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
409
	}
410
411
	while (@ob_end_clean());
412
	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 408
  2. $location is passed through strtr()
    in other/upgrade.php on line 412

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

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

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...
3505
	</div>';
3506
}
3507
3508
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 (L1900-1949) 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...
3509
{
3510
	global $upcontext, $disable_security, $settings, $txt;
3511
3512
	echo '
3513
		<script src="http://www.simplemachines.org/smf/current-version.js?version=' . SMF_VERSION . '"></script>
3514
			<h3>', sprintf($txt['upgrade_ready_proceed'], SMF_VERSION), '</h3>
3515
	<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">
3516
		<input type="hidden" name="', $upcontext['login_token_var'], '" value="', $upcontext['login_token'], '">
3517
		<div id="version_warning" style="margin: 2ex; padding: 2ex; border: 2px dashed #a92174; color: black; background-color: #fbbbe2; display: none;">
3518
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3519
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3520
			<div style="padding-left: 6ex;">
3521
				', sprintf($txt['upgrade_warning_out_of_date'], SMF_VERSION), '
3522
			</div>
3523
		</div>';
3524
3525
	$upcontext['chmod_in_form'] = true;
3526
	template_chmod();
3527
3528
	// For large, pre 1.1 RC2 forums give them a warning about the possible impact of this upgrade!
3529
	if ($upcontext['is_large_forum'])
3530
		echo '
3531
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3532
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3533
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3534
			<div style="padding-left: 6ex;">
3535
				', $txt['upgrade_warning_lots_data'], '
3536
			</div>
3537
		</div>';
3538
3539
	// A warning message?
3540
	if (!empty($upcontext['warning']))
3541
		echo '
3542
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3543
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3544
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3545
			<div style="padding-left: 6ex;">
3546
				', $upcontext['warning'], '
3547
			</div>
3548
		</div>';
3549
3550
	// Paths are incorrect?
3551
	echo '
3552
		<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">
3553
			<div style="float: left; width: 2ex; font-size: 2em; color: black;">!!</div>
3554
			<strong style="text-decoration: underline;">', $txt['upgrade_critical_error'], '</strong><br>
3555
			<div style="padding-left: 6ex;">
3556
				', $txt['upgrade_error_script_js'], '
3557
			</div>
3558
		</div>';
3559
3560
	// Is there someone already doing this?
3561
	if (!empty($upcontext['user']['id']) && (time() - $upcontext['started'] < 72600 || time() - $upcontext['updated'] < 3600))
3562
	{
3563
		$ago = time() - $upcontext['started'];
3564
		if ($ago < 60)
3565
			$ago = $ago . ' seconds';
3566
		elseif ($ago < 3600)
3567
			$ago = (int) ($ago / 60) . ' minutes';
3568
		else
3569
			$ago = (int) ($ago / 3600) . ' hours';
3570
3571
		$active = time() - $upcontext['updated'];
3572
		if ($active < 60)
3573
			$updated = $active . ' seconds';
3574
		elseif ($active < 3600)
3575
			$updated = (int) ($active / 60) . ' minutes';
3576
		else
3577
			$updated = (int) ($active / 3600) . ' hours';
3578
3579
		echo '
3580
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3581
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3582
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3583
			<div style="padding-left: 6ex;">
3584
				&quot;', $upcontext['user']['name'], '&quot; has been running the upgrade script for the last ', $ago, ' - and was last active ', $updated, ' ago.';
3585
3586
		if ($active < 600)
3587
			echo '
3588
				We recommend that you do not run this script unless you are sure that ', $upcontext['user']['name'], ' has completed their upgrade.';
3589
3590
		if ($active > $upcontext['inactive_timeout'])
3591
			echo '
3592
				<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.';
3593
		else
3594
			echo '
3595
				<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!');
3596
3597
		echo '
3598
			</div>
3599
		</div>';
3600
	}
3601
3602
	echo '
3603
			<strong>Admin Login: ', $disable_security ? '(DISABLED)' : '', '</strong>
3604
			<h3>For security purposes please login with your admin account to proceed with the upgrade.</h3>
3605
			<table>
3606
				<tr valign="top">
3607
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Username:</strong></td>
3608
					<td>
3609
						<input type="text" name="user" value="', !empty($upcontext['username']) ? $upcontext['username'] : '', '"', $disable_security ? ' disabled' : '', ' class="input_text">';
3610
3611
	if (!empty($upcontext['username_incorrect']))
3612
		echo '
3613
						<div class="smalltext" style="color: red;">Username Incorrect</div>';
3614
3615
	echo '
3616
					</td>
3617
				</tr>
3618
				<tr valign="top">
3619
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Password:</strong></td>
3620
					<td>
3621
						<input type="password" name="passwrd" value=""', $disable_security ? ' disabled' : '', ' class="input_password">
3622
						<input type="hidden" name="hash_passwrd" value="">';
3623
3624
	if (!empty($upcontext['password_failed']))
3625
		echo '
3626
						<div class="smalltext" style="color: red;">Password Incorrect</div>';
3627
3628
	echo '
3629
					</td>
3630
				</tr>';
3631
3632
	// Can they continue?
3633
	if (!empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] >= $upcontext['inactive_timeout'] && $upcontext['user']['step'] > 1)
3634
	{
3635
		echo '
3636
				<tr>
3637
					<td colspan="2">
3638
						<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>
3639
					</td>
3640
				</tr>';
3641
	}
3642
3643
	echo '
3644
			</table><br>
3645
			<span class="smalltext">
3646
				<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.
3647
			</span>
3648
			<input type="hidden" name="login_attempt" id="login_attempt" value="1">
3649
			<input type="hidden" name="js_works" id="js_works" value="0">';
3650
3651
	// Say we want the continue button!
3652
	$upcontext['continue'] = !empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] < $upcontext['inactive_timeout'] ? 2 : 1;
3653
3654
	// This defines whether javascript is going to work elsewhere :D
3655
	echo '
3656
		<script>
3657
			if (\'XMLHttpRequest\' in window && document.getElementById(\'js_works\'))
3658
				document.getElementById(\'js_works\').value = 1;
3659
3660
			// Latest version?
3661
			function smfCurrentVersion()
3662
			{
3663
				var smfVer, yourVer;
3664
3665
				if (!(\'smfVersion\' in window))
3666
					return;
3667
3668
				window.smfVersion = window.smfVersion.replace(/SMF\s?/g, \'\');
3669
3670
				smfVer = document.getElementById(\'smfVersion\');
3671
				yourVer = document.getElementById(\'yourVersion\');
3672
3673
				setInnerHTML(smfVer, window.smfVersion);
3674
3675
				var currentVersion = getInnerHTML(yourVer);
3676
				if (currentVersion < window.smfVersion)
3677
					document.getElementById(\'version_warning\').style.display = \'\';
3678
			}
3679
			addLoadEvent(smfCurrentVersion);
3680
3681
			// This checks that the script file even exists!
3682
			if (typeof(smfSelectText) == \'undefined\')
3683
				document.getElementById(\'js_script_missing_error\').style.display = \'\';
3684
3685
		</script>';
3686
}
3687
3688
function template_upgrade_options()
3689
{
3690
	global $upcontext, $modSettings, $db_prefix, $mmessage, $mtitle, $db_type;
3691
3692
	echo '
3693
			<h3>Before the upgrade gets underway please review the options below - and hit continue when you\'re ready to begin.</h3>
3694
			<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">';
3695
3696
	// Warning message?
3697
	if (!empty($upcontext['upgrade_options_warning']))
3698
		echo '
3699
		<div style="margin: 1ex; padding: 1ex; border: 1px dashed #cc3344; color: black; background-color: #ffe4e9;">
3700
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3701
			<strong style="text-decoration: underline;">Warning!</strong><br>
3702
			<div style="padding-left: 4ex;">
3703
				', $upcontext['upgrade_options_warning'], '
3704
			</div>
3705
		</div>';
3706
3707
	echo '
3708
				<table>
3709
					<tr valign="top">
3710
						<td width="2%">
3711
							<input type="checkbox" name="backup" id="backup" value="1" class="input_check">
3712
						</td>
3713
						<td width="100%">
3714
							<label for="backup">Backup tables in your database with the prefix &quot;backup_' . $db_prefix . '&quot;.</label> (recommended!)
3715
						</td>
3716
					</tr>
3717
					<tr valign="top">
3718
						<td width="2%">
3719
							<input type="checkbox" name="maint" id="maint" value="1" checked class="input_check">
3720
						</td>
3721
						<td width="100%">
3722
							<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>
3723
							<div id="mainmess" style="display: none;">
3724
								<strong class="smalltext">Maintenance Title: </strong><br>
3725
								<input type="text" name="maintitle" size="30" value="', htmlspecialchars($mtitle), '" class="input_text"><br>
3726
								<strong class="smalltext">Maintenance Message: </strong><br>
3727
								<textarea name="mainmessage" rows="3" cols="50">', htmlspecialchars($mmessage), '</textarea>
3728
							</div>
3729
						</td>
3730
					</tr>
3731
					<tr valign="top">
3732
						<td width="2%">
3733
							<input type="checkbox" name="debug" id="debug" value="1" class="input_check">
3734
						</td>
3735
						<td width="100%">
3736
							<label for="debug">Output extra debugging information</label>
3737
						</td>
3738
					</tr>
3739
					<tr valign="top">
3740
						<td width="2%">
3741
							<input type="checkbox" name="empty_error" id="empty_error" value="1" class="input_check">
3742
						</td>
3743
						<td width="100%">
3744
							<label for="empty_error">Empty error log before upgrading</label>
3745
						</td>
3746
					</tr>';
3747
3748
	if (!empty($upcontext['karma_installed']['good']) || !empty($upcontext['karma_installed']['bad']))
3749
		echo '
3750
					<tr valign="top">
3751
						<td width="2%">
3752
							<input type="checkbox" name="delete_karma" id="delete_karma" value="1" class="input_check">
3753
						</td>
3754
						<td width="100%">
3755
							<label for="delete_karma">Delete all karma settings and info from the DB</label>
3756
						</td>
3757
					</tr>';
3758
3759
	echo '
3760
					<tr valign="top">
3761
						<td width="2%">
3762
							<input type="checkbox" name="stat" id="stat" value="1"', empty($modSettings['allow_sm_stats']) ? '' : ' checked', ' class="input_check">
3763
						</td>
3764
						<td width="100%">
3765
							<label for="stat">
3766
								Allow Simple Machines to Collect Basic Stats Monthly.<br>
3767
								<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>
3768
							</label>
3769
						</td>
3770
					</tr>
3771
				</table>
3772
				<input type="hidden" name="upcont" value="1">';
3773
3774
	// We need a normal continue button here!
3775
	$upcontext['continue'] = 1;
3776
}
3777
3778
// Template for the database backup tool/
3779
function template_backup_database()
3780
{
3781
	global $upcontext, $support_js, $is_debug;
3782
3783
	echo '
3784
			<h3>Please wait while a backup is created. For large forums this may take some time!</h3>';
3785
3786
	echo '
3787
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
3788
			<input type="hidden" name="backup_done" id="backup_done" value="0">
3789
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
3790
			<div id="debug_section" style="height: 200px; overflow: auto;">
3791
			<span id="debuginfo"></span>
3792
			</div>';
3793
3794
	// Dont any tables so far?
3795
	if (!empty($upcontext['previous_tables']))
3796
		foreach ($upcontext['previous_tables'] as $table)
3797
			echo '
3798
			<br>Completed Table: &quot;', $table, '&quot;.';
3799
3800
	echo '
3801
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
3802
			<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>';
3803
3804
	// Continue please!
3805
	$upcontext['continue'] = $support_js ? 2 : 1;
3806
3807
	// If javascript allows we want to do this using XML.
3808
	if ($support_js)
3809
	{
3810
		echo '
3811
		<script>
3812
			var lastTable = ', $upcontext['cur_table_num'], ';
3813
			function getNextTables()
3814
			{
3815
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
3816
			}
3817
3818
			// Got an update!
3819
			function onBackupUpdate(oXMLDoc)
3820
			{
3821
				var sCurrentTableName = "";
3822
				var iTableNum = 0;
3823
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
3824
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
3825
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
3826
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
3827
3828
				// Update the page.
3829
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
3830
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
3831
				lastTable = iTableNum;
3832
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
3833
3834
		// If debug flood the screen.
3835
		if ($is_debug)
3836
			echo '
3837
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
3838
3839
				if (document.getElementById(\'debug_section\').scrollHeight)
3840
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
3841
3842
		echo '
3843
				// Get the next update...
3844
				if (iTableNum == ', $upcontext['table_count'], ')
3845
				{
3846
					document.getElementById(\'commess\').style.display = "";
3847
					document.getElementById(\'current_tab_div\').style.display = "none";
3848
					document.getElementById(\'contbutt\').disabled = 0;
3849
					document.getElementById(\'backup_done\').value = 1;
3850
				}
3851
				else
3852
					getNextTables();
3853
			}
3854
			getNextTables();
3855
		</script>';
3856
	}
3857
}
3858
3859
function template_backup_xml()
3860
{
3861
	global $upcontext;
3862
3863
	echo '
3864
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
3865
}
3866
3867
// Here is the actual "make the changes" template!
3868
function template_database_changes()
3869
{
3870
	global $upcontext, $support_js, $is_debug, $timeLimitThreshold;
3871
3872
	if (empty($is_debug) && !empty($upcontext['upgrade_status']['debug']))
3873
		$is_debug = true;
3874
3875
	echo '
3876
		<h3>Executing database changes</h3>
3877
		<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>';
3878
3879
	echo '
3880
		<form action="', $upcontext['form_url'], '&amp;filecount=', $upcontext['file_count'], '" name="upform" id="upform" method="post">
3881
		<input type="hidden" name="database_done" id="database_done" value="0">';
3882
3883
	// No javascript looks rubbish!
3884
	if (!$support_js)
3885
	{
3886
		foreach ($upcontext['actioned_items'] as $num => $item)
3887
		{
3888
			if ($num != 0)
3889
				echo ' Successful!';
3890
			echo '<br>' . $item;
3891
		}
3892 View Code Duplication
		if (!empty($upcontext['changes_complete']))
3893
		{
3894
			if ($is_debug)
3895
			{
3896
				$active = time() - $upcontext['started'];
3897
				$hours = floor($active / 3600);
3898
				$minutes = intval(($active / 60) % 60);
3899
				$seconds = intval($active % 60);
3900
3901
				$totalTime = '';
3902
				if ($hours > 0)
3903
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
3904
				if ($minutes > 0)
3905
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
3906
				if ($seconds > 0)
3907
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
3908
			}
3909
3910
			if ($is_debug && !empty($totalTime))
3911
				echo ' Successful! Completed in ', $totalTime, '<br><br>';
3912
			else
3913
				echo ' Successful!<br><br>';
3914
3915
			echo '<span id="commess" style="font-weight: bold;">1 Database Updates Complete! Click Continue to Proceed.</span><br>';
3916
		}
3917
	}
3918
	else
3919
	{
3920
		// Tell them how many files we have in total.
3921
		if ($upcontext['file_count'] > 1)
3922
			echo '
3923
		<strong id="info1">Executing upgrade script <span id="file_done">', $upcontext['cur_file_num'], '</span> of ', $upcontext['file_count'], '.</strong>';
3924
3925
		echo '
3926
		<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>
3927
		<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>';
3928
3929 View Code Duplication
		if ($is_debug)
3930
		{
3931
			if ($upcontext['current_debug_item_num'] == $upcontext['debug_items'])
3932
			{
3933
				$active = time() - $upcontext['started'];
3934
				$hours = floor($active / 3600);
3935
				$minutes = intval(($active / 60) % 60);
3936
				$seconds = intval($active % 60);
3937
3938
				$totalTime = '';
3939
				if ($hours > 0)
3940
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
3941
				if ($minutes > 0)
3942
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
3943
				if ($seconds > 0)
3944
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
3945
			}
3946
3947
			echo '
3948
			<br><span id="upgradeCompleted">';
3949
3950
			if (!empty($totalTime))
3951
				echo 'Completed in ', $totalTime, '<br>';
3952
3953
			echo '</span>
3954
			<div id="debug_section" style="height: 200px; overflow: auto;">
3955
			<span id="debuginfo"></span>
3956
			</div>';
3957
		}
3958
	}
3959
3960
	// Place for the XML error message.
3961
	echo '
3962
		<div id="error_block" style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9; display: ', empty($upcontext['error_message']) ? 'none' : '', ';">
3963
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3964
			<strong style="text-decoration: underline;">Error!</strong><br>
3965
			<div style="padding-left: 6ex;" id="error_message">', isset($upcontext['error_message']) ? $upcontext['error_message'] : 'Unknown Error!', '</div>
3966
		</div>';
3967
3968
	// We want to continue at some point!
3969
	$upcontext['continue'] = $support_js ? 2 : 1;
3970
3971
	// If javascript allows we want to do this using XML.
3972
	if ($support_js)
3973
	{
3974
		echo '
3975
		<script>
3976
			var lastItem = ', $upcontext['current_debug_item_num'], ';
3977
			var sLastString = "', strtr($upcontext['current_debug_item_name'], array('"' => '&quot;')), '";
3978
			var iLastSubStepProgress = -1;
3979
			var curFile = ', $upcontext['cur_file_num'], ';
3980
			var totalItems = 0;
3981
			var prevFile = 0;
3982
			var retryCount = 0;
3983
			var testvar = 0;
3984
			var timeOutID = 0;
3985
			var getData = "";
3986
			var debugItems = ', $upcontext['debug_items'], ';';
3987
3988
		if ($is_debug)
3989
			echo '
3990
			var upgradeStartTime = ' . $upcontext['started'] . ';';
3991
3992
		echo '
3993
			function getNextItem()
3994
			{
3995
				// We want to track this...
3996
				if (timeOutID)
3997
					clearTimeout(timeOutID);
3998
				timeOutID = window.setTimeout("retTimeout()", ', (10 * $timeLimitThreshold), '000);
3999
4000
				getXMLDocument(\'', $upcontext['form_url'], '&xml&filecount=', $upcontext['file_count'], '&substep=\' + lastItem + getData, onItemUpdate);
4001
			}
4002
4003
			// Got an update!
4004
			function onItemUpdate(oXMLDoc)
4005
			{
4006
				var sItemName = "";
4007
				var sDebugName = "";
4008
				var iItemNum = 0;
4009
				var iSubStepProgress = -1;
4010
				var iDebugNum = 0;
4011
				var bIsComplete = 0;
4012
				getData = "";
4013
4014
				// We\'ve got something - so reset the timeout!
4015
				if (timeOutID)
4016
					clearTimeout(timeOutID);
4017
4018
				// Assume no error at this time...
4019
				document.getElementById("error_block").style.display = "none";
4020
4021
				// Are we getting some duff info?
4022
				if (!oXMLDoc.getElementsByTagName("item")[0])
4023
				{
4024
					// Too many errors?
4025
					if (retryCount > 15)
4026
					{
4027
						document.getElementById("error_block").style.display = "";
4028
						setInnerHTML(document.getElementById("error_message"), "Error retrieving information on step: " + (sDebugName == "" ? sLastString : sDebugName));';
4029
4030
	if ($is_debug)
4031
		echo '
4032
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4033
4034
	echo '
4035
					}
4036
					else
4037
					{
4038
						retryCount++;
4039
						getNextItem();
4040
					}
4041
					return false;
4042
				}
4043
4044
				// Never allow loops.
4045
				if (curFile == prevFile)
4046
				{
4047
					retryCount++;
4048
					if (retryCount > 10)
4049
					{
4050
						document.getElementById("error_block").style.display = "";
4051
						setInnerHTML(document.getElementById("error_message"), "Upgrade script appears to be going into a loop - step: " + sDebugName);';
4052
4053
	if ($is_debug)
4054
		echo '
4055
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4056
4057
	echo '
4058
					}
4059
				}
4060
				retryCount = 0;
4061
4062
				for (var i = 0; i < oXMLDoc.getElementsByTagName("item")[0].childNodes.length; i++)
4063
					sItemName += oXMLDoc.getElementsByTagName("item")[0].childNodes[i].nodeValue;
4064
				for (var i = 0; i < oXMLDoc.getElementsByTagName("debug")[0].childNodes.length; i++)
4065
					sDebugName += oXMLDoc.getElementsByTagName("debug")[0].childNodes[i].nodeValue;
4066
				for (var i = 0; i < oXMLDoc.getElementsByTagName("get").length; i++)
4067
				{
4068
					getData += "&" + oXMLDoc.getElementsByTagName("get")[i].getAttribute("key") + "=";
4069
					for (var j = 0; j < oXMLDoc.getElementsByTagName("get")[i].childNodes.length; j++)
4070
					{
4071
						getData += oXMLDoc.getElementsByTagName("get")[i].childNodes[j].nodeValue;
4072
					}
4073
				}
4074
4075
				iItemNum = oXMLDoc.getElementsByTagName("item")[0].getAttribute("num");
4076
				iDebugNum = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("num"));
4077
				bIsComplete = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("complete"));
4078
				iSubStepProgress = parseFloat(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("percent"));
4079
				sLastString = sDebugName + " (Item: " + iDebugNum + ")";
4080
4081
				curFile = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("num"));
4082
				debugItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("debug_items"));
4083
				totalItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("items"));
4084
4085
				// If we have an error we haven\'t completed!
4086
				if (oXMLDoc.getElementsByTagName("error")[0] && bIsComplete)
4087
					iDebugNum = lastItem;
4088
4089
				// Do we have the additional progress bar?
4090
				if (iSubStepProgress != -1)
4091
				{
4092
					document.getElementById("substep_bar_div").style.display = "";
4093
					document.getElementById("substep_bar_div2").style.display = "";
4094
					document.getElementById("substep_progress").style.width = iSubStepProgress + "%";
4095
					setInnerHTML(document.getElementById("substep_text"), iSubStepProgress + "%");
4096
					setInnerHTML(document.getElementById("substep_bar_div"), sDebugName.replace(/\./g, "") + ":");
4097
				}
4098
				else
4099
				{
4100
					document.getElementById("substep_bar_div").style.display = "none";
4101
					document.getElementById("substep_bar_div2").style.display = "none";
4102
				}
4103
4104
				// Move onto the next item?
4105
				if (bIsComplete)
4106
					lastItem = iDebugNum;
4107
				else
4108
					lastItem = iDebugNum - 1;
4109
4110
				// Are we finished?
4111
				if (bIsComplete && iDebugNum == -1 && curFile >= ', $upcontext['file_count'], ')
4112
				{';
4113
4114
		if ($is_debug)
4115
			echo '
4116
					document.getElementById(\'debug_section\').style.display = "none";
4117
4118
					var upgradeFinishedTime = parseInt(oXMLDoc.getElementsByTagName("curtime")[0].childNodes[0].nodeValue);
4119
					var diffTime = upgradeFinishedTime - upgradeStartTime;
4120
					var diffHours = Math.floor(diffTime / 3600);
4121
					var diffMinutes = parseInt((diffTime / 60) % 60);
4122
					var diffSeconds = parseInt(diffTime % 60);
4123
4124
					var totalTime = "";
4125
					if (diffHours > 0)
4126
						totalTime = totalTime + diffHours + " hour" + (diffHours > 1 ? "s" : "") + " ";
4127
					if (diffMinutes > 0)
4128
						totalTime = totalTime + diffMinutes + " minute" + (diffMinutes > 1 ? "s" : "") + " ";
4129
					if (diffSeconds > 0)
4130
						totalTime = totalTime + diffSeconds + " second" + (diffSeconds > 1 ? "s" : "");
4131
4132
					setInnerHTML(document.getElementById("upgradeCompleted"), "Completed in " + totalTime);';
4133
4134
		echo '
4135
4136
					document.getElementById(\'commess\').style.display = "";
4137
					document.getElementById(\'contbutt\').disabled = 0;
4138
					document.getElementById(\'database_done\').value = 1;';
4139
4140
		if ($upcontext['file_count'] > 1)
4141
			echo '
4142
					document.getElementById(\'info1\').style.display = "none";';
4143
4144
		echo '
4145
					document.getElementById(\'info2\').style.display = "none";
4146
					updateStepProgress(100, 100, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4147
					return true;
4148
				}
4149
				// Was it the last step in the file?
4150
				else if (bIsComplete && iDebugNum == -1)
4151
				{
4152
					lastItem = 0;
4153
					prevFile = curFile;';
4154
4155
		if ($is_debug)
4156
			echo '
4157
					setOuterHTML(document.getElementById(\'debuginfo\'), \'Moving to next script file...done<br><span id="debuginfo"><\' + \'/span>\');';
4158
4159
		echo '
4160
					getNextItem();
4161
					return true;
4162
				}';
4163
4164
		// If debug scroll the screen.
4165
		if ($is_debug)
4166
			echo '
4167
				if (iLastSubStepProgress == -1)
4168
				{
4169
					// Give it consistent dots.
4170
					dots = sDebugName.match(/\./g);
4171
					numDots = dots ? dots.length : 0;
4172
					for (var i = numDots; i < 3; i++)
4173
						sDebugName += ".";
4174
					setOuterHTML(document.getElementById(\'debuginfo\'), sDebugName + \'<span id="debuginfo"><\' + \'/span>\');
4175
				}
4176
				iLastSubStepProgress = iSubStepProgress;
4177
4178
				if (bIsComplete)
4179
					setOuterHTML(document.getElementById(\'debuginfo\'), \'done<br><span id="debuginfo"><\' + \'/span>\');
4180
				else
4181
					setOuterHTML(document.getElementById(\'debuginfo\'), \'...<span id="debuginfo"><\' + \'/span>\');
4182
4183
				if (document.getElementById(\'debug_section\').scrollHeight)
4184
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4185
4186
		echo '
4187
				// Update the page.
4188
				setInnerHTML(document.getElementById(\'item_num\'), iItemNum);
4189
				setInnerHTML(document.getElementById(\'cur_item_name\'), sItemName);';
4190
4191
		if ($upcontext['file_count'] > 1)
4192
		{
4193
			echo '
4194
				setInnerHTML(document.getElementById(\'file_done\'), curFile);
4195
				setInnerHTML(document.getElementById(\'item_count\'), totalItems);';
4196
		}
4197
4198
		echo '
4199
				// Is there an error?
4200
				if (oXMLDoc.getElementsByTagName("error")[0])
4201
				{
4202
					var sErrorMsg = "";
4203
					for (var i = 0; i < oXMLDoc.getElementsByTagName("error")[0].childNodes.length; i++)
4204
						sErrorMsg += oXMLDoc.getElementsByTagName("error")[0].childNodes[i].nodeValue;
4205
					document.getElementById("error_block").style.display = "";
4206
					setInnerHTML(document.getElementById("error_message"), sErrorMsg);
4207
					return false;
4208
				}
4209
4210
				// Get the progress bar right.
4211
				barTotal = debugItems * ', $upcontext['file_count'], ';
4212
				barDone = (debugItems * (curFile - 1)) + lastItem;
4213
4214
				updateStepProgress(barDone, barTotal, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4215
4216
				// Finally - update the time here as it shows the server is responding!
4217
				curTime = new Date();
4218
				iElapsed = (curTime.getTime() / 1000 - ', $upcontext['started'], ');
4219
				mins = parseInt(iElapsed / 60);
4220
				secs = parseInt(iElapsed - mins * 60);
4221
				setInnerHTML(document.getElementById("mins_elapsed"), mins);
4222
				setInnerHTML(document.getElementById("secs_elapsed"), secs);
4223
4224
				getNextItem();
4225
				return true;
4226
			}
4227
4228
			// What if we timeout?!
4229
			function retTimeout(attemptAgain)
4230
			{
4231
				// Oh noes...
4232
				if (!attemptAgain)
4233
				{
4234
					document.getElementById("error_block").style.display = "";
4235
					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");
4236
				}
4237
				else
4238
				{
4239
					document.getElementById("error_block").style.display = "none";
4240
					getNextItem();
4241
				}
4242
			}';
4243
4244
		// Start things off assuming we've not errored.
4245
		if (empty($upcontext['error_message']))
4246
			echo '
4247
			getNextItem();';
4248
4249
		echo '
4250
		</script>';
4251
	}
4252
	return;
4253
}
4254
4255
function template_database_xml()
4256
{
4257
	global $is_debug, $upcontext;
4258
4259
	echo '
4260
	<file num="', $upcontext['cur_file_num'], '" items="', $upcontext['total_items'], '" debug_items="', $upcontext['debug_items'], '">', $upcontext['cur_file_name'], '</file>
4261
	<item num="', $upcontext['current_item_num'], '">', $upcontext['current_item_name'], '</item>
4262
	<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>';
4263
4264
	if (!empty($upcontext['error_message']))
4265
		echo '
4266
	<error>', $upcontext['error_message'], '</error>';
4267
4268
	if (!empty($upcontext['error_string']))
4269
		echo '
4270
	<sql>', $upcontext['error_string'], '</sql>';
4271
4272
	if ($is_debug)
4273
		echo '
4274
	<curtime>', time(), '</curtime>';
4275
}
4276
4277
// Template for the UTF-8 conversion step. Basically a copy of the backup stuff with slight modifications....
4278 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...
4279
{
4280
	global $upcontext, $support_js, $is_debug;
4281
4282
	echo '
4283
			<h3>Please wait while your database is converted to UTF-8. For large forums this may take some time!</h3>';
4284
4285
	echo '
4286
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4287
			<input type="hidden" name="utf8_done" id="utf8_done" value="0">
4288
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
4289
			<span id="debuginfo"></span>';
4290
4291
	// Done any tables so far?
4292
	if (!empty($upcontext['previous_tables']))
4293
		foreach ($upcontext['previous_tables'] as $table)
4294
			echo '
4295
			<br>Completed Table: &quot;', $table, '&quot;.';
4296
4297
	echo '
4298
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>';
4299
4300
	// If we dropped their index, let's let them know
4301
	if ($upcontext['cur_table_num'] == $upcontext['table_count'] && $upcontext['dropping_index'])
4302
		echo '
4303
			<br><span style="display:inline;">Please note that your fulltext index was dropped to facilitate the conversion and will need to be recreated.</span>';
4304
4305
	echo '
4306
			<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>';
4307
4308
	// Continue please!
4309
	$upcontext['continue'] = $support_js ? 2 : 1;
4310
4311
	// If javascript allows we want to do this using XML.
4312
	if ($support_js)
4313
	{
4314
		echo '
4315
		<script>
4316
			var lastTable = ', $upcontext['cur_table_num'], ';
4317
			function getNextTables()
4318
			{
4319
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4320
			}
4321
4322
			// Got an update!
4323
			function onBackupUpdate(oXMLDoc)
4324
			{
4325
				var sCurrentTableName = "";
4326
				var iTableNum = 0;
4327
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4328
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4329
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4330
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4331
4332
				// Update the page.
4333
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4334
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4335
				lastTable = iTableNum;
4336
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4337
4338
		// If debug flood the screen.
4339
		if ($is_debug)
4340
			echo '
4341
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');';
4342
4343
		echo '
4344
				// Get the next update...
4345
				if (iTableNum == ', $upcontext['table_count'], ')
4346
				{
4347
					document.getElementById(\'commess\').style.display = "";
4348
					document.getElementById(\'current_tab_div\').style.display = "none";
4349
					document.getElementById(\'contbutt\').disabled = 0;
4350
					document.getElementById(\'utf8_done\').value = 1;
4351
				}
4352
				else
4353
					getNextTables();
4354
			}
4355
			getNextTables();
4356
		</script>';
4357
	}
4358
}
4359
4360
function template_utf8_xml()
4361
{
4362
	global $upcontext;
4363
4364
	echo '
4365
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4366
}
4367
4368
// Template for the database backup tool/
4369 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...
4370
{
4371
	global $upcontext, $support_js, $is_debug;
4372
4373
	echo '
4374
			<h3>Converting data from serialize to JSON...</h3>';
4375
4376
	echo '
4377
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4378
			<input type="hidden" name="json_done" id="json_done" value="0">
4379
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
4380
			<span id="debuginfo"></span>';
4381
4382
	// Dont any tables so far?
4383
	if (!empty($upcontext['previous_tables']))
4384
		foreach ($upcontext['previous_tables'] as $table)
4385
			echo '
4386
			<br>Completed Table: &quot;', $table, '&quot;.';
4387
4388
	echo '
4389
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
4390
			<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>';
4391
4392
	// Try to make sure substep was reset.
4393
	if ($upcontext['cur_table_num'] == $upcontext['table_count'])
4394
		echo '
4395
			<input type="hidden" name="substep" id="substep" value="0">';
4396
4397
	// Continue please!
4398
	$upcontext['continue'] = $support_js ? 2 : 1;
4399
4400
	// If javascript allows we want to do this using XML.
4401
	if ($support_js)
4402
	{
4403
		echo '
4404
		<script>
4405
			var lastTable = ', $upcontext['cur_table_num'], ';
4406
			function getNextTables()
4407
			{
4408
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4409
			}
4410
4411
			// Got an update!
4412
			function onBackupUpdate(oXMLDoc)
4413
			{
4414
				var sCurrentTableName = "";
4415
				var iTableNum = 0;
4416
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4417
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4418
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4419
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4420
4421
				// Update the page.
4422
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4423
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4424
				lastTable = iTableNum;
4425
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4426
4427
		// If debug flood the screen.
4428
		if ($is_debug)
4429
			echo '
4430
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');';
4431
4432
		echo '
4433
				// Get the next update...
4434
				if (iTableNum == ', $upcontext['table_count'], ')
4435
				{
4436
					document.getElementById(\'commess\').style.display = "";
4437
					document.getElementById(\'current_tab_div\').style.display = "none";
4438
					document.getElementById(\'contbutt\').disabled = 0;
4439
					document.getElementById(\'json_done\').value = 1;
4440
				}
4441
				else
4442
					getNextTables();
4443
			}
4444
			getNextTables();
4445
		</script>';
4446
	}
4447
}
4448
4449
function template_serialize_json_xml()
4450
{
4451
	global $upcontext;
4452
4453
	echo '
4454
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4455
}
4456
4457
function template_upgrade_complete()
4458
{
4459
	global $upcontext, $upgradeurl, $settings, $boardurl, $is_debug;
4460
4461
	echo '
4462
	<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>
4463
	<form action="', $boardurl, '/index.php">';
4464
4465
	if (!empty($upcontext['can_delete_script']))
4466
		echo '
4467
			<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>
4468
			<script>
4469
				function doTheDelete(theCheck)
4470
				{
4471
					var theImage = document.getElementById ? document.getElementById("delete_upgrader") : document.all.delete_upgrader;
4472
4473
					theImage.src = "', $upgradeurl, '?delete=1&ts_" + (new Date().getTime());
4474
					theCheck.disabled = true;
4475
				}
4476
			</script>
4477
			<img src="', $settings['default_theme_url'], '/images/blank.png" alt="" id="delete_upgrader"><br>';
4478
4479
	$active = time() - $upcontext['started'];
4480
	$hours = floor($active / 3600);
4481
	$minutes = intval(($active / 60) % 60);
4482
	$seconds = intval($active % 60);
4483
4484
	if ($is_debug)
4485
	{
4486
		$totalTime = '';
4487
		if ($hours > 0)
4488
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
4489
		if ($minutes > 0)
4490
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
4491
		if ($seconds > 0)
4492
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
4493
	}
4494
4495
	if ($is_debug && !empty($totalTime))
4496
		echo '<br> Upgrade completed in ', $totalTime, '<br><br>';
4497
4498
	echo '<br>
4499
			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>
4500
			<br>
4501
			Best of luck,<br>
4502
			Simple Machines';
4503
}
4504
4505
/**
4506
 * Convert MySQL (var)char ip col to binary
4507
 *
4508
 * @param string $targetTable The table to perform the operation on
4509
 * @param string $oldCol The old column to gather data from
4510
 * @param string $newCol The new column to put data in
4511
 * @param int $limit The amount of entries to handle at once.
4512
 * @param int $setSize The amount of entries after which to update the database.
4513
 *
4514
 * newCol needs to be a varbinary(16) null able field
4515
 * @return bool
4516
 */
4517
function MySQLConvertOldIp($targetTable, $oldCol, $newCol, $limit = 50000, $setSize = 100)
4518
{
4519
	global $smcFunc, $step_progress;
4520
4521
	$current_substep = $_GET['substep'];
4522
4523
	$step_progress['name'] = 'Converting ips';
4524
	$step_progress['current'] = !empty($_GET['a']) ? $_GET['a'] : 0;
4525
4526
	// Skip this if we don't have the column
4527
	$request = $smcFunc['db_query']('', '
4528
		SHOW FIELDS
4529
		FROM {db_prefix}{raw:table}
4530
		WHERE Field = {string:name}',
4531
		array(
4532
			'table' => $targetTable,
4533
			'name' => $oldCol,
4534
	));
4535
	if ($smcFunc['db_num_rows']($request) !== 1)
4536
	{
4537
		$smcFunc['db_free_result']($request);
4538
		return;
4539
	}
4540
	$smcFunc['db_free_result']($request);
4541
4542
	//mysql default max length is 1mb http://dev.mysql.com/doc/refman/5.1/en/packet-too-large.html
4543
	$arIp = array();
4544
4545
	$is_done = false;
4546
	while (!$is_done)
4547
	{
4548
		// Keep looping at the current step.
4549
		nextSubStep($current_substep);
4550
4551
		$request = $smcFunc['db_query']('', '
4552
			SELECT DISTINCT {raw:old_col}
4553
			FROM {db_prefix}{raw:table_name}
4554
			WHERE {raw:new_col} IS NULL
4555
			LIMIT {int:limit}',
4556
			array(
4557
				'old_col' => $oldCol,
4558
				'new_col' => $newCol,
4559
				'table_name' => $targetTable,
4560
				'empty' => '',
4561
				'limit' => $limit,
4562
		));
4563
		while ($row = $smcFunc['db_fetch_assoc']($request))
4564
			$arIp[] = $row[$oldCol];
4565
		$smcFunc['db_free_result']($request);
4566
4567
		// Special case, null ip could keep us in a loop.
4568
		if (is_null($arIp[0]))
4569
			unset($arIp[0]);
4570
4571
		if (empty($arIp))
4572
			$is_done = true;
4573
4574
		$updates = array();
4575
		$cases = array();
4576
		$count = count($arIp);
4577
		for ($i = 0; $i < $count; $i++)
4578
		{
4579
			$arIp[$i] = trim($arIp[$i]);
4580
4581
			if (empty($arIp[$i]))
4582
				continue;
4583
4584
			$updates['ip' . $i] = $arIp[$i];
4585
			$cases[$arIp[$i]] = 'WHEN ' . $oldCol . ' = {string:ip' . $i . '} THEN {inet:ip' . $i . '}';
4586
4587
			if ($setSize > 0 && $i % $setSize === 0)
4588
			{
4589
				if (count($updates) == 1)
4590
					continue;
4591
4592
				$updates['whereSet'] = array_values($updates);
4593
				$smcFunc['db_query']('', '
4594
					UPDATE {db_prefix}' . $targetTable . '
4595
					SET ' . $newCol . ' = CASE ' .
4596
					implode('
4597
						', $cases) . '
4598
						ELSE NULL
4599
					END
4600
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4601
					$updates
4602
				);
4603
4604
				$updates = array();
4605
				$cases = array();
4606
			}
4607
		}
4608
4609
		// Incase some extras made it through.
4610
		if (!empty($updates))
4611
		{
4612
			if (count($updates) == 1)
4613
			{
4614
				foreach ($updates as $key => $ip)
4615
				{
4616
					$smcFunc['db_query']('', '
4617
						UPDATE {db_prefix}' . $targetTable . '
4618
						SET ' . $newCol . ' = {inet:ip}
4619
						WHERE ' . $oldCol . ' = {string:ip}',
4620
						array(
4621
							'ip' => $ip
4622
					));
4623
				}
4624
			}
4625
			else
4626
			{
4627
				$updates['whereSet'] = array_values($updates);
4628
				$smcFunc['db_query']('', '
4629
					UPDATE {db_prefix}' . $targetTable . '
4630
					SET ' . $newCol . ' = CASE ' .
4631
					implode('
4632
						', $cases) . '
4633
						ELSE NULL
4634
					END
4635
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4636
					$updates
4637
				);
4638
			}
4639
		}
4640
		else
4641
			$is_done = true;
4642
4643
		$_GET['a'] += $limit;
4644
		$step_progress['current'] = $_GET['a'];
4645
	}
4646
4647
	unset($_GET['a']);
4648
}
4649
4650
/**
4651
 * Get the column info. This is basically the same as smf_db_list_columns but we get 1 column, force detail and other checks.
4652
 *
4653
 * @param string $targetTable The table to perform the operation on
4654
 * @param string $column The column we are looking for.
4655
 *
4656
 * @return array Info on the table.
4657
 */
4658
function upgradeGetColumnInfo($targetTable, $column)
4659
{
4660
 	global $smcFunc;
4661
4662
 	// This should already be here, but be safe.
4663
 	db_extend('packages');
4664
 
4665
 	$columns = $smcFunc['db_list_columns']($targetTable, true);
4666
4667
	if (isset($columns[$column]))
4668
		return $columns[$column];
4669
	else
4670
		return null;
4671
}
4672
4673
?>
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...