Completed
Push — release-2.1 ( f9d087...e49a83 )
by Mert
04:30
created

upgrade.php ➔ clean_cache()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 22
Code Lines 7

Duplication

Lines 5
Ratio 22.73 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 2
nop 1
dl 5
loc 22
rs 9.2
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 2016 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
$GLOBALS['required_php_version'] = '5.3.8';
19
$GLOBALS['required_mysql_version'] = '5.0.3';
20
21
$databases = array(
22
	'mysqli' => array(
23
		'name' => 'MySQLi',
24
		'version' => '5.0.3',
25
		'version_check' => 'global $db_connection; return min(mysqli_get_server_info($db_connection), mysqli_get_client_info());',
26
		'utf8_support' => true,
27
		'utf8_version' => '5.0.3',
28
		'utf8_version_check' => 'global $db_connection; return mysqli_get_server_info($db_connection);',
29
		'alter_support' => true,
30
	),
31
	'mysql' => array(
32
		'name' => 'MySQL',
33
		'version' => '5.0.3',
34
		'version_check' => 'return min(mysql_get_server_info(), mysql_get_client_info());',
35
		'utf8_support' => true,
36
		'utf8_version' => '5.0.3',
37
		'utf8_version_check' => 'return mysql_get_server_info();',
38
		'alter_support' => true,
39
	),
40
	'postgresql' => array(
41
		'name' => 'PostgreSQL',
42
		'version' => '8.0',
43
		'version_check' => '$version = pg_version(); return $version[\'client\'];',
44
		'always_has_db' => true,
45
	),
46
);
47
48
// General options for the script.
49
$timeLimitThreshold = 3;
50
$upgrade_path = dirname(__FILE__);
51
$upgradeurl = $_SERVER['PHP_SELF'];
52
// Where the SMF images etc are kept.
53
$smfsite = 'http://www.simplemachines.org/smf';
54
// Disable the need for admins to login?
55
$disable_security = false;
56
// How long, in seconds, must admin be inactive to allow someone else to run?
57
$upcontext['inactive_timeout'] = 10;
58
59
// All the steps in detail.
60
// Number,Name,Function,Progress Weight.
61
$upcontext['steps'] = array(
62
	0 => array(1, 'Login', 'WelcomeLogin', 2),
63
	1 => array(2, 'Upgrade Options', 'UpgradeOptions', 2),
64
	2 => array(3, 'Backup', 'BackupDatabase', 10),
65
	3 => array(4, 'Database Changes', 'DatabaseChanges', 50),
66
	4 => array(5, 'Convert to UTF-8', 'ConvertUtf8', 20),
67
	5 => array(6, 'Convert serialized strings to JSON', 'serialize_to_json', 10),
68
	// This is removed as it doesn't really work right at the moment.
69
	//4 => array(5, 'Cleanup Mods', 'CleanupMods', 10),
70
	6 => array(7, 'Delete Upgrade.php', 'DeleteUpgrade', 1),
71
);
72
// Just to remember which one has files in it.
73
$upcontext['database_step'] = 3;
74
@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...
75
if (!ini_get('safe_mode'))
76
{
77
	ini_set('mysql.connect_timeout', -1);
78
	ini_set('default_socket_timeout', 900);
79
}
80
// Clean the upgrade path if this is from the client.
81
if (!empty($_SERVER['argv']) && php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
82
	for ($i = 1; $i < $_SERVER['argc']; $i++)
83
	{
84
		if (preg_match('~^--path=(.+)$~', $_SERVER['argv'][$i], $match) != 0)
85
			$upgrade_path = substr($match[1], -1) == '/' ? substr($match[1], 0, -1) : $match[1];
86
	}
87
88
// Are we from the client?
89
if (php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
90
{
91
	$command_line = true;
92
	$disable_security = 1;
93
}
94
else
95
	$command_line = false;
96
97
// Load this now just because we can.
98
require_once($upgrade_path . '/Settings.php');
99
100
// Are we logged in?
101
if (isset($upgradeData))
102
{
103
	$upcontext['user'] = unserialize(base64_decode($upgradeData));
104
105
	// Check for sensible values.
106 View Code Duplication
	if (empty($upcontext['user']['started']) || $upcontext['user']['started'] < time() - 86400)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

Loading history...
109
		$upcontext['user']['updated'] = 0;
110
111
	$upcontext['started'] = $upcontext['user']['started'];
112
	$upcontext['updated'] = $upcontext['user']['updated'];
113
114
	$is_debug = !empty($upcontext['user']['debug']) ? true : false;
115
}
116
117
// Nothing sensible?
118
if (empty($upcontext['updated']))
119
{
120
	$upcontext['started'] = time();
121
	$upcontext['updated'] = 0;
122
	$upcontext['user'] = array(
123
		'id' => 0,
124
		'name' => 'Guest',
125
		'pass' => 0,
126
		'started' => $upcontext['started'],
127
		'updated' => $upcontext['updated'],
128
	);
129
}
130
131
// Load up some essential data...
132
loadEssentialData();
133
134
// Are we going to be mimic'ing SSI at this point?
135
if (isset($_GET['ssi']))
136
{
137
	require_once($sourcedir . '/Errors.php');
138
	require_once($sourcedir . '/Logging.php');
139
	require_once($sourcedir . '/Load.php');
140
	require_once($sourcedir . '/Security.php');
141
	require_once($sourcedir . '/Subs-Package.php');
142
143
	loadUserSettings();
144
	loadPermissions();
145
}
146
147
if (!function_exists('un_htmlspecialchars'))
148
{
149
	function un_htmlspecialchars($string)
150
	{
151
		return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, ENT_QUOTES)) + array('&#039;' => '\'', '&nbsp;' => ' '));
152
	}
153
}
154
155
if (!function_exists('text2words'))
156
{
157
	function text2words($text)
158
	{
159
		// Step 1: Remove entities/things we don't consider words:
160
		$words = preg_replace('~(?:[\x0B\0\xA0\t\r\s\n(){}\\[\\]<>!@$%^*.,:+=`\~\?/\\\\]+|&(?:amp|lt|gt|quot);)+~', ' ', $text);
161
162
		// Step 2: Entities we left to letters, where applicable, lowercase.
163
		$words = preg_replace('~([^&\d]|^)[#;]~', '$1 ', un_htmlspecialchars(strtolower($words)));
164
165
		// Step 3: Ready to split apart and index!
166
		$words = explode(' ', $words);
167
		$returned_words = array();
168
		foreach ($words as $word)
169
		{
170
			$word = trim($word, '-_\'');
171
172
			if ($word != '')
173
				$returned_words[] = substr($word, 0, 20);
174
		}
175
176
		return array_unique($returned_words);
177
	}
178
}
179
180
if (!function_exists('clean_cache'))
181
{
182
	// Empty out the cache folder.
183
	function clean_cache($type = '')
184
	{
185
		global $cachedir, $sourcedir;
186
187
		// No directory = no game.
188
		if (!is_dir($cachedir))
189
			return;
190
191
		// Remove the files in SMF's own disk cache, if any
192
		$dh = opendir($cachedir);
193 View Code Duplication
		while ($file = readdir($dh))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
194
		{
195
			if ($file != '.' && $file != '..' && $file != 'index.php' && $file != '.htaccess' && (!$type || substr($file, 0, strlen($type)) == $type))
196
				@unlink($cachedir . '/' . $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...
197
		}
198
		closedir($dh);
199
200
		// Invalidate cache, to be sure!
201
		// ... as long as index.php can be modified, anyway.
202
		@touch($cachedir . '/' . 'index.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...
203
		clearstatcache();
204
	}
205
}
206
207
// MD5 Encryption.
208
if (!function_exists('md5_hmac'))
209
{
210
	function md5_hmac($data, $key)
211
	{
212
		if (strlen($key) > 64)
213
			$key = pack('H*', md5($key));
214
		$key = str_pad($key, 64, chr(0x00));
215
216
		$k_ipad = $key ^ str_repeat(chr(0x36), 64);
217
		$k_opad = $key ^ str_repeat(chr(0x5c), 64);
218
219
		return md5($k_opad . pack('H*', md5($k_ipad . $data)));
220
	}
221
}
222
223
// Don't do security check if on Yabbse
224
if (!isset($modSettings['smfVersion']))
225
	$disable_security = true;
226
227
// This only exists if we're on SMF ;)
228
if (isset($modSettings['smfVersion']))
229
{
230
	$request = $smcFunc['db_query']('', '
231
		SELECT variable, value
232
		FROM {db_prefix}themes
233
		WHERE id_theme = {int:id_theme}
234
			AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
235
		array(
236
			'id_theme' => 1,
237
			'theme_url' => 'theme_url',
238
			'theme_dir' => 'theme_dir',
239
			'images_url' => 'images_url',
240
			'db_error_skip' => true,
241
		)
242
	);
243 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
244
		$modSettings[$row['variable']] = $row['value'];
245
	$smcFunc['db_free_result']($request);
246
}
247
248
if (!isset($modSettings['theme_url']))
249
{
250
	$modSettings['theme_dir'] = $boarddir . '/Themes/default';
251
	$modSettings['theme_url'] = 'Themes/default';
252
	$modSettings['images_url'] = 'Themes/default/images';
253
}
254
if (!isset($settings['default_theme_url']))
255
	$settings['default_theme_url'] = $modSettings['theme_url'];
256
if (!isset($settings['default_theme_dir']))
257
	$settings['default_theme_dir'] = $modSettings['theme_dir'];
258
259
$upcontext['is_large_forum'] = (empty($modSettings['smfVersion']) || $modSettings['smfVersion'] <= '1.1 RC1') && !empty($modSettings['totalMessages']) && $modSettings['totalMessages'] > 75000;
260
// Default title...
261
$upcontext['page_title'] = isset($modSettings['smfVersion']) ? 'Updating Your SMF Install!' : 'Upgrading from YaBB SE!';
262
263
// Have we got tracking data - if so use it (It will be clean!)
264
if (isset($_GET['data']))
265
{
266
	global $is_debug;
267
268
	$upcontext['upgrade_status'] = safe_unserialize(base64_decode($_GET['data']));
269
	$upcontext['current_step'] = $upcontext['upgrade_status']['curstep'];
270
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
271
	$upcontext['rid'] = $upcontext['upgrade_status']['rid'];
272
	$support_js = $upcontext['upgrade_status']['js'];
273
274
	// Only set this if the upgrader status says so.
275
	if (empty($is_debug))
276
		$is_debug = $upcontext['upgrade_status']['debug'];
277
278
	// Load the language.
279 View Code Duplication
	if (file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
280
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
281
}
282
// Set the defaults.
283
else
284
{
285
	$upcontext['current_step'] = 0;
286
	$upcontext['rid'] = mt_rand(0, 5000);
287
	$upcontext['upgrade_status'] = array(
288
		'curstep' => 0,
289
		'lang' => isset($_GET['lang']) ? $_GET['lang'] : basename($language, '.lng'),
290
		'rid' => $upcontext['rid'],
291
		'pass' => 0,
292
		'debug' => 0,
293
		'js' => 0,
294
	);
295
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
296
}
297
298
// If this isn't the first stage see whether they are logging in and resuming.
299
if ($upcontext['current_step'] != 0 || !empty($upcontext['user']['step']))
300
	checkLogin();
301
302
if ($command_line)
303
	cmdStep0();
304
305
// Don't error if we're using xml.
306
if (isset($_GET['xml']))
307
	$upcontext['return_error'] = true;
308
309
// Loop through all the steps doing each one as required.
310
$upcontext['overall_percent'] = 0;
311
foreach ($upcontext['steps'] as $num => $step)
312
{
313
	if ($num >= $upcontext['current_step'])
314
	{
315
		// The current weight of this step in terms of overall progress.
316
		$upcontext['step_weight'] = $step[3];
317
		// Make sure we reset the skip button.
318
		$upcontext['skip'] = false;
319
320
		// We cannot proceed if we're not logged in.
321
		if ($num != 0 && !$disable_security && $upcontext['user']['pass'] != $upcontext['upgrade_status']['pass'])
322
		{
323
			$upcontext['steps'][0][2]();
324
			break;
325
		}
326
327
		// Call the step and if it returns false that means pause!
328 View Code Duplication
		if (function_exists($step[2]) && $step[2]() === false)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
329
			break;
330
		elseif (function_exists($step[2]))
331
			$upcontext['current_step']++;
332
	}
333
	$upcontext['overall_percent'] += $step[3];
334
}
335
336
upgradeExit();
337
338
// Exit the upgrade script.
339
function upgradeExit($fallThrough = false)
340
{
341
	global $upcontext, $upgradeurl, $boarddir, $command_line, $is_debug;
342
343
	// Save where we are...
344
	if (!empty($upcontext['current_step']) && !empty($upcontext['user']['id']))
345
	{
346
		$upcontext['user']['step'] = $upcontext['current_step'];
347
		$upcontext['user']['substep'] = $_GET['substep'];
348
		$upcontext['user']['updated'] = time();
349
		$upcontext['debug'] = $is_debug;
350
		$upgradeData = base64_encode(safe_serialize($upcontext['user']));
351
		copy($boarddir . '/Settings.php', $boarddir . '/Settings_bak.php');
352
		changeSettings(array('upgradeData' => '"' . $upgradeData . '"'));
353
		updateLastError();
354
	}
355
356
	// Handle the progress of the step, if any.
357
	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
358
	{
359
		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
360
		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
361
	}
362
	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
363
364
	// We usually dump our templates out.
365
	if (!$fallThrough)
366
	{
367
		// This should not happen my dear... HELP ME DEVELOPERS!!
368
		if (!empty($command_line))
369
		{
370
			if (function_exists('debug_print_backtrace'))
371
				debug_print_backtrace();
372
373
			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.';
374
			flush();
375
			die();
376
		}
377
378
		if (!isset($_GET['xml']))
379
			template_upgrade_above();
380
		else
381
		{
382
			header('Content-Type: text/xml; charset=UTF-8');
383
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
384
			$upcontext['get_data'] = array();
385
			foreach ($_GET as $k => $v)
386
			{
387
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
388
				{
389
					$upcontext['get_data'][$k] = $v;
390
				}
391
			}
392
			template_xml_above();
393
		}
394
395
		// Call the template.
396
		if (isset($upcontext['sub_template']))
397
		{
398
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
399
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(safe_serialize($upcontext['upgrade_status']));
400
401
			// Custom stuff to pass back?
402
			if (!empty($upcontext['query_string']))
403
				$upcontext['form_url'] .= $upcontext['query_string'];
404
405
			call_user_func('template_' . $upcontext['sub_template']);
406
		}
407
408
		// Was there an error?
409
		if (!empty($upcontext['forced_error_message']))
410
			echo $upcontext['forced_error_message'];
411
412
		// Show the footer.
413
		if (!isset($_GET['xml']))
414
			template_upgrade_below();
415
		else
416
			template_xml_below();
417
	}
418
419
420
	if (!empty($command_line) && $is_debug)
421
	{
422
		$active = time() - $upcontext['started'];
423
		$hours = floor($active / 3600);
424
		$minutes = intval(($active / 60) % 60);
425
		$seconds = intval($active % 60);
426
427
		$totalTime = '';
428
		if ($hours > 0)
429
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's':'') . ' ';
430
		if ($minutes > 0)
431
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's':'') . ' ';
432
		if ($seconds > 0)
433
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's':'') . ' ';
434
435
		if (!empty($totalTime))
436
			echo "\n" . 'Upgrade completed in ' . $totalTime . "\n";
437
	}
438
439
	// Bang - gone!
440
	die();
441
}
442
443
// Used to direct the user to another location.
444
function redirectLocation($location, $addForm = true)
445
{
446
	global $upgradeurl, $upcontext, $command_line;
447
448
	// Command line users can't be redirected.
449
	if ($command_line)
450
		upgradeExit(true);
451
452
	// Are we providing the core info?
453
	if ($addForm)
454
	{
455
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
456
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(safe_serialize($upcontext['upgrade_status'])) . $location;
457
	}
458
459
	while (@ob_end_clean());
460
	header('Location: ' . strtr($location, array('&amp;' => '&')));
461
462
	// Exit - saving status as we go.
463
	upgradeExit(true);
464
}
465
466
// Load all essential data and connect to the DB as this is pre SSI.php
467
function loadEssentialData()
468
{
469
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection, $db_prefix, $db_character_set, $db_type;
470
	global $modSettings, $sourcedir, $smcFunc;
471
472
	// Do the non-SSI stuff...
473
	if (function_exists('set_magic_quotes_runtime'))
474
		@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...
475
476
	error_reporting(E_ALL);
477
	define('SMF', 1);
478
479
	// Start the session.
480
	if (@ini_get('session.save_handler') == 'user')
481
		@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...
482
	@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...
483
484
	if (empty($smcFunc))
485
		$smcFunc = array();
486
487
	// We need this for authentication and some upgrade code
488
	require_once($sourcedir . '/Subs-Auth.php');
489
	require_once($sourcedir . '/Class-Package.php');
490
491
	$smcFunc['strtolower'] = 'smf_strtolower';
492
493
494
	// Initialize everything...
495
	initialize_inputs();
496
497
	// Get the database going!
498
	if (empty($db_type))
499
		$db_type = 'mysql';
500
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
501
	{
502
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
503
504
		// Make the connection...
505
		$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, array('non_fatal' => true));
506
507
		// Oh dear god!!
508
		if ($db_connection === null)
509
			die('Unable to connect to database - please check username and password are correct in Settings.php');
510
511
		if (($db_type == 'mysql' || $db_type == 'mysqli') && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
512
			$smcFunc['db_query']('', '
513
			SET NAMES {string:db_character_set}',
514
			array(
515
				'db_error_skip' => true,
516
				'db_character_set' => $db_character_set,
517
			)
518
		);
519
520
		// Load the modSettings data...
521
		$request = $smcFunc['db_query']('', '
522
			SELECT variable, value
523
			FROM {db_prefix}settings',
524
			array(
525
				'db_error_skip' => true,
526
			)
527
		);
528
		$modSettings = array();
529 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
530
			$modSettings[$row['variable']] = $row['value'];
531
		$smcFunc['db_free_result']($request);
532
	}
533
	else
534
	{
535
		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.');
536
	}
537
538
	require_once($sourcedir . '/Subs.php');
539
540
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
541
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
542
	{
543
		require_once($sourcedir . '/QueryString.php');
544
		cleanRequest();
545
	}
546
547
	if (!isset($_GET['substep']))
548
		$_GET['substep'] = 0;
549
}
550
551
function initialize_inputs()
0 ignored issues
show
Best Practice introduced by
The function initialize_inputs() has been defined more than once; this definition is ignored, only the first definition in other/install.php (L152-263) 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...
552
{
553
	global $start_time, $upcontext, $db_type;
554
555
	$start_time = time();
556
557
	umask(0);
558
559
	// Fun.  Low PHP version...
560
	if (!isset($_GET))
561
	{
562
		$GLOBALS['_GET']['step'] = 0;
563
		return;
564
	}
565
566
	ob_start();
567
568
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
569
	ignore_user_abort(true);
570
571
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
572
	if (isset($_GET['delete']))
573
	{
574
		@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...
575
576
		$type = ($db_type == 'mysqli' ? 'mysql' : $db_type);
577
578
		// And the extra little files ;).
579
		@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...
580
		@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...
581
		@unlink(dirname(__FILE__) . '/upgrade_2-0_' . $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...
582
		@unlink(dirname(__FILE__) . '/upgrade_2-1_' . $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...
583
584
		$dh = opendir(dirname(__FILE__));
585
		while ($file = readdir($dh))
586
		{
587
			if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
588
				@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...
589
		}
590
		closedir($dh);
591
592
		// Legacy files while we're at it. NOTE: We only touch files we KNOW shouldn't be there.
593
		// 1.1 Sources files not in 2.0+
594
		@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...
595
		// 1.1 Templates that don't exist any more (e.g. renamed)
596
		@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...
597
		@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...
598
		// 1.1 JS files were stored in the main theme folder, but in 2.0+ are in the scripts/ folder
599
		@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...
600
		@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...
601
		@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...
602
		@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...
603
		@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...
604
605
		// 2.0 Sources files not in 2.1+
606
		@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...
607
		@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...
608
609
		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');
610
		exit;
611
	}
612
613
	// Anybody home?
614 View Code Duplication
	if (!isset($_GET['xml']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
615
	{
616
		$upcontext['remote_files_available'] = false;
617
		$test = @fsockopen('www.simplemachines.org', 80, $errno, $errstr, 1);
618
		if ($test)
619
			$upcontext['remote_files_available'] = true;
620
		@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...
621
	}
622
623
	// Something is causing this to happen, and it's annoying.  Stop it.
624
	$temp = 'upgrade_php?step';
625
	while (strlen($temp) > 4)
626
	{
627
		if (isset($_GET[$temp]))
628
			unset($_GET[$temp]);
629
		$temp = substr($temp, 1);
630
	}
631
632
	// Force a step, defaulting to 0.
633
	$_GET['step'] = (int) @$_GET['step'];
634
	$_GET['substep'] = (int) @$_GET['substep'];
635
}
636
637
// Step 0 - Let's welcome them in and ask them to login!
638
function WelcomeLogin()
639
{
640
	global $boarddir, $sourcedir, $modSettings, $cachedir, $upgradeurl, $upcontext;
641
	global $smcFunc, $db_type, $databases, $txt, $boardurl;
642
643
	$upcontext['sub_template'] = 'welcome_message';
644
645
	$type = ($db_type == 'mysqli' ? 'mysql' : $db_type);
646
647
	// Check for some key files - one template, one language, and a new and an old source file.
648
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
649
		&& @file_exists($sourcedir . '/QueryString.php')
650
		&& @file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')
651
		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $type . '.sql');
652
653
	// Need legacy scripts?
654 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.1)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

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

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

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

Loading history...
659
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
660
661
	// This needs to exist!
662
	if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
663
		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>]');
664
	else
665
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
666
667
	if (!$check)
668
		// 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.
669
		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.');
670
671
	// Do they meet the install requirements?
672
	if (!php_version_check())
673
		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.');
674
675
	if (!db_version_check())
676
		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.');
677
678
	// Do some checks to make sure they have proper privileges
679
	db_extend('packages');
680
681
	// CREATE
682
	$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');
683
684
	// ALTER
685
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
686
687
	// DROP
688
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
689
690
	// Sorry... we need CREATE, ALTER and DROP
691 View Code Duplication
	if (!$create || !$alter || !$drop)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
692
		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.');
693
694
	// Do a quick version spot check.
695
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
696
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
697
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
698
		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.');
699
700
	// What absolutely needs to be writable?
701
	$writable_files = array(
702
		$boarddir . '/Settings.php',
703
		$boarddir . '/Settings_bak.php',
704
		$boarddir . '/db_last_error.php',
705
		$modSettings['theme_dir'] . '/css/minified.css',
706
		$modSettings['theme_dir'] . '/scripts/minified.js',
707
		$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
708
	);
709
710
	// Do we need to add this setting?
711
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
712
713
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] .'/custom_avatar';
714
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl .'/custom_avatar';
715
716
	// This little fellow has to cooperate...
717
	quickFileWritable($custom_av_dir);
718
719
	// Are we good now?
720
	if(!is_writable($custom_av_dir))
721
		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));
722
	elseif ($need_settings_update)
723
	{
724
		if (!function_exists('cache_put_data'))
725
			require_once($sourcedir . '/Load.php');
726
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
727
		updateSettings(array('custom_avatar_url' => $custom_av_url));
728
	}
729
730
	require_once($sourcedir . '/Security.php');
731
732
	// Check the cache directory.
733
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
734
	if (!file_exists($cachedir_temp))
735
		@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...
736
	if (!file_exists($cachedir_temp))
737
		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.');
738
739
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
740
		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>.');
741
	elseif (!isset($_GET['skiplang']))
742
	{
743
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
744
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
745
746
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
747
			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>]');
748
	}
749
750
	if (!makeFilesWritable($writable_files))
751
		return false;
752
753
	// Check agreement.txt. (it may not exist, in which case $boarddir must be writable.)
754 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
755
		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.');
756
757
	// Upgrade the agreement.
758
	elseif (isset($modSettings['agreement']))
759
	{
760
		$fp = fopen($boarddir . '/agreement.txt', 'w');
761
		fwrite($fp, $modSettings['agreement']);
762
		fclose($fp);
763
	}
764
765
	// We're going to check that their board dir setting is right in case they've been moving stuff around.
766
	if (strtr($boarddir, array('/' => '', '\\' => '')) != strtr(dirname(__FILE__), array('/' => '', '\\' => '')))
767
		$upcontext['warning'] = '
768
			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>
769
			<ul>
770
				<li>Board Directory: ' . $boarddir . '</li>
771
				<li>Source Directory: ' . $boarddir . '</li>
772
				<li>Cache Directory: ' . $cachedir_temp . '</li>
773
			</ul>
774
			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.';
775
776
	// Either we're logged in or we're going to present the login.
777
	if (checkLogin())
778
		return true;
779
780
	$upcontext += createToken('login');
781
782
	return false;
783
}
784
785
// Step 0.5: Does the login work?
786
function checkLogin()
787
{
788
	global $modSettings, $upcontext, $disable_security;
789
	global $smcFunc, $db_type, $support_js;
790
791
	// Are we trying to login?
792
	if (isset($_POST['contbutt']) && (!empty($_POST['user']) || $disable_security))
793
	{
794
		// If we've disabled security pick a suitable name!
795
		if (empty($_POST['user']))
796
			$_POST['user'] = 'Administrator';
797
798
		// Before 2.0 these column names were different!
799
		$oldDB = false;
800
		if (empty($db_type) || $db_type == 'mysql' || $db_type == 'mysqli')
801
		{
802
			$request = $smcFunc['db_query']('', '
803
				SHOW COLUMNS
804
				FROM {db_prefix}members
805
				LIKE {string:member_name}',
806
				array(
807
					'member_name' => 'memberName',
808
					'db_error_skip' => true,
809
				)
810
			);
811
			if ($smcFunc['db_num_rows']($request) != 0)
812
				$oldDB = true;
813
			$smcFunc['db_free_result']($request);
814
		}
815
816
		// Get what we believe to be their details.
817
		if (!$disable_security)
818
		{
819
			if ($oldDB)
820
				$request = $smcFunc['db_query']('', '
821
					SELECT id_member, memberName AS member_name, passwd, id_group,
822
					additionalGroups AS additional_groups, lngfile
823
					FROM {db_prefix}members
824
					WHERE memberName = {string:member_name}',
825
					array(
826
						'member_name' => $_POST['user'],
827
						'db_error_skip' => true,
828
					)
829
				);
830
			else
831
				$request = $smcFunc['db_query']('', '
832
					SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile
833
					FROM {db_prefix}members
834
					WHERE member_name = {string:member_name}',
835
					array(
836
						'member_name' => $_POST['user'],
837
						'db_error_skip' => true,
838
					)
839
				);
840
			if ($smcFunc['db_num_rows']($request) != 0)
841
			{
842
				list ($id_member, $name, $password, $id_group, $addGroups, $user_language) = $smcFunc['db_fetch_row']($request);
843
844
				$groups = explode(',', $addGroups);
845
				$groups[] = $id_group;
846
847
				foreach ($groups as $k => $v)
848
					$groups[$k] = (int) $v;
849
850
				$sha_passwd = sha1(strtolower($name) . un_htmlspecialchars($_REQUEST['passwrd']));
851
			}
852
			else
853
				$upcontext['username_incorrect'] = true;
854
			$smcFunc['db_free_result']($request);
855
		}
856
		$upcontext['username'] = $_POST['user'];
857
858
		// Track whether javascript works!
859
		if (!empty($_POST['js_works']))
860
		{
861
			$upcontext['upgrade_status']['js'] = 1;
862
			$support_js = 1;
863
		}
864
		else
865
			$support_js = 0;
866
867
		// Note down the version we are coming from.
868
		if (!empty($modSettings['smfVersion']) && empty($upcontext['user']['version']))
869
			$upcontext['user']['version'] = $modSettings['smfVersion'];
870
871
		// Didn't get anywhere?
872
		if (!$disable_security && (empty($sha_passwd) || (!empty($password) ? $password : '') != $sha_passwd) && !hash_verify_password((!empty($name) ? $name : ''), $_REQUEST['passwrd'], (!empty($password) ? $password : '')) && empty($upcontext['username_incorrect']))
0 ignored issues
show
Bug introduced by
The variable $sha_passwd does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
873
		{
874
			// MD5?
875
			$md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user']));
876
			if ($md5pass != $password)
0 ignored issues
show
Bug introduced by
The variable $password does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
877
			{
878
				$upcontext['password_failed'] = true;
879
				// Disable the hashing this time.
880
				$upcontext['disable_login_hashing'] = true;
881
			}
882
		}
883
884
		if ((empty($upcontext['password_failed']) && !empty($name)) || $disable_security)
885
		{
886
			// Set the password.
887
			if (!$disable_security)
888
			{
889
				// Do we actually have permission?
890
				if (!in_array(1, $groups))
891
				{
892
					$request = $smcFunc['db_query']('', '
893
						SELECT permission
894
						FROM {db_prefix}permissions
895
						WHERE id_group IN ({array_int:groups})
896
							AND permission = {string:admin_forum}',
897
						array(
898
							'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...
899
							'admin_forum' => 'admin_forum',
900
							'db_error_skip' => true,
901
						)
902
					);
903
					if ($smcFunc['db_num_rows']($request) == 0)
904
						return throw_error('You need to be an admin to perform an upgrade!');
905
					$smcFunc['db_free_result']($request);
906
				}
907
908
				$upcontext['user']['id'] = $id_member;
0 ignored issues
show
Bug introduced by
The variable $id_member does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
909
				$upcontext['user']['name'] = $name;
910
			}
911
			else
912
			{
913
				$upcontext['user']['id'] = 1;
914
				$upcontext['user']['name'] = 'Administrator';
915
			}
916
			$upcontext['user']['pass'] = mt_rand(0,60000);
917
			// This basically is used to match the GET variables to Settings.php.
918
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
919
920
			// Set the language to that of the user?
921
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
922
			{
923
				$user_language = basename($user_language, '.lng');
924
				$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
925
				preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
926
927
				if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
928
					$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'] . '.';
929
				elseif (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . basename($user_language, '.lng') . '.php'))
930
					$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'] . '.';
931
				else
932
				{
933
					// Set this as the new language.
934
					$upcontext['language'] = $user_language;
935
					$upcontext['upgrade_status']['lang'] = $upcontext['language'];
936
937
					// Include the file.
938
					require_once($modSettings['theme_dir'] . '/languages/Install.' . $user_language . '.php');
939
				}
940
			}
941
942
			// If we're resuming set the step and substep to be correct.
943
			if (isset($_POST['cont']))
944
			{
945
				$upcontext['current_step'] = $upcontext['user']['step'];
946
				$_GET['substep'] = $upcontext['user']['substep'];
947
			}
948
949
			return true;
950
		}
951
	}
952
953
	return false;
954
}
955
956
// Step 1: Do the maintenance and backup.
957
function UpgradeOptions()
958
{
959
	global $db_prefix, $command_line, $modSettings, $is_debug, $smcFunc, $packagesdir, $tasksdir;
960
	global $boarddir, $boardurl, $sourcedir, $maintenance, $cachedir, $upcontext, $db_type, $db_server, $db_last_error;
961
962
	$upcontext['sub_template'] = 'upgrade_options';
963
	$upcontext['page_title'] = 'Upgrade Options';
964
965
	db_extend('packages');
966
	$upcontext['karma_installed'] = array('good' => false, 'bad' => false);
967
	$member_columns = $smcFunc['db_list_columns']('{db_prefix}members');
968
969
	$upcontext['karma_installed']['good'] = in_array('karma_good', $member_columns);
970
	$upcontext['karma_installed']['bad'] = in_array('karma_bad', $member_columns);
971
972
	unset($member_columns);
973
974
	// If we've not submitted then we're done.
975
	if (empty($_POST['upcont']))
976
		return false;
977
978
	require_once($sourcedir . '/Subs-Admin.php');
979
	updateSettingsFile(array('image_proxy_secret' => '\'' . substr(sha1(mt_rand()), 0, 20) . '\''));
980
981
	// Firstly, if they're enabling SM stat collection just do it.
982
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']))
983
	{
984
		// Attempt to register the site etc.
985
		$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
986
		if ($fp)
987
		{
988
			$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
989
			$out .= 'Host: www.simplemachines.org' . "\r\n";
990
			$out .= 'Connection: Close' . "\r\n\r\n";
991
			fwrite($fp, $out);
992
993
			$return_data = '';
994
			while (!feof($fp))
995
				$return_data .= fgets($fp, 128);
996
997
			fclose($fp);
998
999
			// Get the unique site ID.
1000
			preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);
1001
1002 View Code Duplication
			if (!empty($ID[1]))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1003
				$smcFunc['db_insert']('replace',
1004
					$db_prefix . 'settings',
1005
					array('variable' => 'string', 'value' => 'string'),
1006
					array('allow_sm_stats', $ID[1]),
1007
					array('variable')
1008
				);
1009
		}
1010
	}
1011
	else
1012
		$smcFunc['db_query']('', '
1013
			DELETE FROM {db_prefix}settings
1014
			WHERE variable = {string:allow_sm_stats}',
1015
			array(
1016
				'allow_sm_stats' => 'allow_sm_stats',
1017
				'db_error_skip' => true,
1018
			)
1019
		);
1020
1021
	// Deleting old karma stuff?
1022
	if (!empty($_POST['delete_karma']))
1023
	{
1024
		// Delete old settings vars.
1025
		$smcFunc['db_query']('', '
1026
			DELETE FROM {db_prefix}settings
1027
			WHERE variable IN ({array_string:karma_vars})',
1028
			array(
1029
				'karma_vars' => array('karmaMode', 'karmaTimeRestrictAdmins', 'karmaWaitTime', 'karmaMinPosts', 'karmaLabel', 'karmaSmiteLabel', 'karmaApplaudLabel'),
1030
			)
1031
		);
1032
1033
		// Cleaning up old karma member settings.
1034
		if ($upcontext['karma_installed']['good'])
1035
			$smcFunc['db_query']('', '
1036
				ALTER TABLE {db_prefix}members
1037
				DROP karma_good',
1038
				array()
1039
			);
1040
1041
		// Does karma bad was enable?
1042
		if ($upcontext['karma_installed']['bad'])
1043
			$smcFunc['db_query']('', '
1044
				ALTER TABLE {db_prefix}members
1045
				DROP karma_bad',
1046
				array()
1047
			);
1048
1049
		// Cleaning up old karma permissions.
1050
		$smcFunc['db_query']('', '
1051
			DELETE FROM {db_prefix}permissions
1052
			WHERE permission = {string:karma_vars}',
1053
			array(
1054
				'karma_vars' => 'karma_edit',
1055
			)
1056
		);
1057
	}
1058
1059
	// Emptying the error log?
1060
	if (!empty($_POST['empty_error']))
1061
		$smcFunc['db_query']('truncate_table', '
1062
			TRUNCATE {db_prefix}log_errors',
1063
			array(
1064
			)
1065
		);
1066
1067
	$changes = array();
1068
1069
	// If we're overriding the language follow it through.
1070 View Code Duplication
	if (isset($_GET['lang']) && file_exists($modSettings['theme_dir'] . '/languages/index.' . $_GET['lang'] . '.php'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

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

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

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

Loading history...
1102
		$changes['sourcedir'] = '\'' . fixRelativePath($sourcedir) . '\'';
1103
1104
	if (empty($cachedir) || substr($cachedir, 0, 1) == '.')
1105
		$changes['cachedir'] = '\'' . fixRelativePath($boarddir) . '/cache\'';
1106
1107
	// Not had the database type added before?
1108
	if (empty($db_type))
1109
		$changes['db_type'] = 'mysql';
1110
1111
	// For now we offer a option, this may change in future versions when mysql is completely removed.
1112
	if (!empty($_POST['convertMysql']) && $db_type == 'mysql')
1113
		$changes['db_type'] = '\'mysqli\'';
1114
1115
	// If they have a "host:port" setup for the host, split that into separate values
1116
	// You should never have a : in the hostname if you're not on MySQL, but better safe than sorry
1117
	if (strpos($db_server, ':') !== false && ($db_type == 'mysql' || $db_type == 'mysqli'))
1118
	{
1119
		list($db_server, $db_port) = explode(':', $db_server);
1120
1121
		$changes['db_server'] = '\'' . $db_server . '\'';
1122
1123
		// Only set this if we're not using the default port
1124
		if ($db_port != ini_get('mysql' . ($db_type == 'mysqli' || !empty($_POST['convertMysql']) ? 'i' : '') . '.default_port'))
1125
			$changes['db_port'] = (int) $db_port;
1126
	}
1127
	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...
1128
	{
1129
		// If db_port is set and is the same as the default, set it to ''
1130
		if ($db_type == 'mysql' || $db_type == 'mysqli')
1131
		{
1132
			if ($db_port == ini_get('mysql' . ($db_type == 'mysqli' || !empty($_POST['convertMysql']) ? 'i' : '') . '.default_port'))
1133
				$changes['db_port'] = '\'\'';
1134
			elseif ($db_type == 'postgresql' && $db_port == 5432)
1135
				$changes['db_port'] = '\'\'';
1136
		}
1137
	}
1138
1139
	// Maybe we haven't had this option yet?
1140
	if (empty($packagesdir))
1141
		$changes['packagesdir'] = '\'' . fixRelativePath($boarddir) . '/Packages\'';
1142
1143
	// Add support for $tasksdir var.
1144
	if (empty($tasksdir))
1145
		$changes['tasksdir'] = '\'' . fixRelativePath($sourcedir) . '/tasks\'';
1146
1147
	// @todo Maybe change the cookie name if going to 1.1, too?
1148
1149
	// Update Settings.php with the new settings.
1150
	changeSettings($changes);
1151
1152
	// Back up again before we do anything else, just in case...
1153
	copy($boarddir . '/Settings.php', $boarddir . '/Settings_bak.php');
1154
1155
	// Read the contents of the file in as a string
1156
	$settings_file = file_get_contents($boarddir . '/Settings.php');
1157
1158
	// Look to see if the new error-catching section is there...
1159
	if (stripos($settings_file, 'if (file_exists(dirname(__FILE__) . \'/db_last_error.php\'))') === false)
1160
	{
1161
		// This is what we want to add...
1162
		$error_catching_header = '
1163
########## Error-Catching ##########
1164
# Note: You shouldn\'t touch these settings.';
1165
1166
		$error_catching = '
1167
if (file_exists(dirname(__FILE__) . \'/db_last_error.php\'))
1168
	include(dirname(__FILE__) . \'/db_last_error.php\');
1169
1170
if (!isset($db_last_error))
1171
{
1172
	// File does not exist so lets try to create it
1173
	file_put_contents(dirname(__FILE__) . \'/db_last_error.php\', \'<\' . \'?\' . "php\n" . \'$db_last_error = 0;\' . "\n" . \'?\' . \'>\');
1174
	$db_last_error = 0;
1175
}
1176
';
1177
		// Before we go any further, check to see if the original code is there first...
1178
		if (stripos('########## Error-Catching ##########', $settings_file !== false))
1179
		{
1180
			$found_old = true;
1181
			// Replace the old line with the new code - assuming the header is already there
1182
			$settings_file = str_replace('$db_last_error = ' . $db_last_error . ';', $error_catching, $settings_file);
1183
		}
1184
		// What about just the db_last_error line?
1185
		elseif (stripos('$db_last_error =', $settings_file !== false))
1186
		{
1187
			$found_old = true;
1188
			// Replace the old line with the new code
1189
			$settings_file = str_replace('$db_last_error = ' . $db_last_error . ';', $error_catching_header . $error_catching, $settings_file);
1190
		}
1191
		else
1192
		{
1193
			$found_old = false;
1194
			// We want the comments as well as the code...
1195
			$error_catching = $error_catching_header . $error_catching;
1196
		}
1197
1198
		// Blank out the file - done to fix a oddity with some servers.
1199
		$fp = fopen($boarddir . '/Settings.php', 'w');
1200
		fclose($fp);
1201
1202
		// Open the file for writing
1203
		$file = fopen($boarddir . '/Settings.php', 'r+');
1204
1205
		// Write the original contents...
1206
		fwrite($file, $settings_file);
1207
1208
		// If we didn't find the old code, add the new code at the end instead...
1209
		if (!$found_old)
1210
		{
1211
			// Go to the position two bytes before the end - in front of the closing PHP tag
1212
			fseek($file, -2, SEEK_END);
1213
1214
			// Append our new data - the extra line break above will prevent us from breaking things...
1215
			fwrite($file, $error_catching);
1216
		}
1217
1218
		// Close the file
1219
		fclose($file);
1220
	}
1221
1222
	if ($command_line)
1223
		echo ' Successful.' . "\n";
1224
1225
	// Are we doing debug?
1226
	if (isset($_POST['debug']))
1227
	{
1228
		$upcontext['upgrade_status']['debug'] = true;
1229
		$is_debug = true;
1230
	}
1231
1232
	// If we're not backing up then jump one.
1233
	if (empty($_POST['backup']))
1234
		$upcontext['current_step']++;
1235
1236
	// If we've got here then let's proceed to the next step!
1237
	return true;
1238
}
1239
1240
// Backup the database - why not...
1241
function BackupDatabase()
1242
{
1243
	global $upcontext, $db_prefix, $command_line, $is_debug, $support_js, $file_steps, $smcFunc;
1244
1245
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1246
	$upcontext['page_title'] = 'Backup Database';
1247
1248
	// Done it already - js wise?
1249
	if (!empty($_POST['backup_done']))
1250
		return true;
1251
1252
	// Some useful stuff here.
1253
	db_extend();
1254
1255
	// Might need this as well
1256
	db_extend('packages');
1257
1258
	// Get all the table names.
1259
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1260
	$db = preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? strtr($match[1], array('`' => '')) : false;
1261
	$tables = $smcFunc['db_list_tables']($db, $filter);
1262
1263
	$table_names = array();
1264
	foreach ($tables as $table)
1265
		if (substr($table, 0, 7) !== 'backup_')
1266
			$table_names[] = $table;
1267
1268
	$upcontext['table_count'] = count($table_names);
1269
	$upcontext['cur_table_num'] = $_GET['substep'];
1270
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1271
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1272
	// For non-java auto submit...
1273
	$file_steps = $upcontext['table_count'];
1274
1275
	// What ones have we already done?
1276 View Code Duplication
	foreach ($table_names as $id => $table)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1277
		if ($id < $_GET['substep'])
1278
			$upcontext['previous_tables'][] = $table;
1279
1280
	if ($command_line)
1281
		echo 'Backing Up Tables.';
1282
1283
	// If we don't support javascript we backup here.
1284
	if (!$support_js || isset($_GET['xml']))
1285
	{
1286
		// Backup each table!
1287
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1288
		{
1289
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1290
			$upcontext['cur_table_num'] = $substep + 1;
1291
1292
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1293
1294
			// Do we need to pause?
1295
			nextSubstep($substep);
1296
1297
			backupTable($table_names[$substep]);
1298
1299
			// If this is XML to keep it nice for the user do one table at a time anyway!
1300
			if (isset($_GET['xml']))
1301
				return upgradeExit();
1302
		}
1303
1304
		if ($command_line)
1305
		{
1306
			echo "\n" . ' Successful.\'' . "\n";
1307
			flush();
1308
		}
1309
		$upcontext['step_progress'] = 100;
1310
1311
		$_GET['substep'] = 0;
1312
		// Make sure we move on!
1313
		return true;
1314
	}
1315
1316
	// Either way next place to post will be database changes!
1317
	$_GET['substep'] = 0;
1318
	return false;
1319
}
1320
1321
// Backup one table...
1322
function backupTable($table)
1323
{
1324
	global $is_debug, $command_line, $db_prefix, $smcFunc;
1325
1326
	if ($command_line)
1327
	{
1328
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1329
		flush();
1330
	}
1331
1332
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1333
1334
	if ($command_line)
1335
		echo ' done.';
1336
}
1337
1338
// Step 2: Everything.
1339
function DatabaseChanges()
1340
{
1341
	global $db_prefix, $modSettings, $command_line, $smcFunc;
1342
	global $upcontext, $support_js, $db_type;
1343
1344
	// Have we just completed this?
1345
	if (!empty($_POST['database_done']))
1346
		return true;
1347
1348
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1349
	$upcontext['page_title'] = 'Database Changes';
1350
1351
	$type = ($db_type == 'mysqli' ? 'mysql' : $db_type);
1352
1353
	// All possible files.
1354
	// Name, <version, insert_on_complete
1355
	$files = array(
1356
		array('upgrade_1-0.sql', '1.1', '1.1 RC0'),
1357
		array('upgrade_1-1.sql', '2.0', '2.0 a'),
1358
		array('upgrade_2-0_' . $type . '.sql', '2.1', '2.1 dev0'),
1359
		array('upgrade_2-1_' . $type . '.sql', '3.0', SMF_VERSION),
1360
	);
1361
1362
	// How many files are there in total?
1363
	if (isset($_GET['filecount']))
1364
		$upcontext['file_count'] = (int) $_GET['filecount'];
1365
	else
1366
	{
1367
		$upcontext['file_count'] = 0;
1368
		foreach ($files as $file)
1369
		{
1370
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1371
				$upcontext['file_count']++;
1372
		}
1373
	}
1374
1375
	// Do each file!
1376
	$did_not_do = count($files) - $upcontext['file_count'];
1377
	$upcontext['step_progress'] = 0;
1378
	$upcontext['cur_file_num'] = 0;
1379
	foreach ($files as $file)
1380
	{
1381
		if ($did_not_do)
1382
			$did_not_do--;
1383
		else
1384
		{
1385
			$upcontext['cur_file_num']++;
1386
			$upcontext['cur_file_name'] = $file[0];
1387
			// Do we actually need to do this still?
1388
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1389
			{
1390
				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1391 View Code Duplication
				if ($nextFile)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1392
				{
1393
					// Only update the version of this if complete.
1394
					$smcFunc['db_insert']('replace',
1395
						$db_prefix . 'settings',
1396
						array('variable' => 'string', 'value' => 'string'),
1397
						array('smfVersion', $file[2]),
1398
						array('variable')
1399
					);
1400
1401
					$modSettings['smfVersion'] = $file[2];
1402
				}
1403
1404
				// If this is XML we only do this stuff once.
1405
				if (isset($_GET['xml']))
1406
				{
1407
					// Flag to move on to the next.
1408
					$upcontext['completed_step'] = true;
1409
					// Did we complete the whole file?
1410
					if ($nextFile)
1411
						$upcontext['current_debug_item_num'] = -1;
1412
					return upgradeExit();
1413
				}
1414
				elseif ($support_js)
1415
					break;
1416
			}
1417
			// Set the progress bar to be right as if we had - even if we hadn't...
1418
			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1419
		}
1420
	}
1421
1422
	$_GET['substep'] = 0;
1423
	// So the template knows we're done.
1424
	if (!$support_js)
1425
	{
1426
		$upcontext['changes_complete'] = true;
1427
1428
		return true;
1429
	}
1430
	return false;
1431
}
1432
1433
// Clean up any mods installed...
1434
function CleanupMods()
1435
{
1436
	global $db_prefix, $upcontext, $boarddir, $packagesdir, $settings, $smcFunc, $command_line;
1437
1438
	// Sorry. Not supported for command line users.
1439
	if ($command_line)
1440
		return true;
1441
1442
	// Skipping first?
1443
	if (!empty($_POST['skip']))
1444
	{
1445
		unset($_POST['skip']);
1446
		return true;
1447
	}
1448
1449
	// If we get here withOUT SSI we need to redirect to ensure we get it!
1450
	if (!isset($_GET['ssi']) || !function_exists('mktree'))
1451
		redirectLocation('&ssi=1');
1452
1453
	$upcontext['sub_template'] = 'clean_mods';
1454
	$upcontext['page_title'] = 'Cleanup Modifications';
1455
1456
	// This can be skipped.
1457
	$upcontext['skip'] = true;
1458
1459
	// If we're on the second redirect continue...
1460
	if (isset($_POST['cleandone2']))
1461
		return true;
1462
1463
	// Do we already know about some writable files?
1464
	if (isset($_POST['writable_files']))
1465
	{
1466
		$writable_files = safe_unserialize(base64_decode($_POST['writable_files']));
1467
		if (!makeFilesWritable($writable_files))
1468
		{
1469
			// What have we left?
1470
			$upcontext['writable_files'] = $writable_files;
1471
			return false;
1472
		}
1473
	}
1474
1475
	// Make sure we have some sort of packages directory.
1476
	if (!isset($packagesdir))
1477
		$packagesdir = $boarddir . '/Packages';
1478
1479
	// Load all theme paths....
1480
	$request = $smcFunc['db_query']('', '
1481
		SELECT id_theme, variable, value
1482
		FROM {db_prefix}themes
1483
		WHERE id_member = {int:id_member}
1484
			AND variable IN ({string:theme_dir}, {string:images_url})',
1485
		array(
1486
			'id_member' => 0,
1487
			'theme_dir' => 'theme_dir',
1488
			'images_url' => 'images_url',
1489
			'db_error_skip' => true,
1490
		)
1491
	);
1492
	$theme_paths = array();
1493
	while ($row = $smcFunc['db_fetch_assoc']($request))
1494
	{
1495
		if ($row['id_theme'] == 1)
1496
			$settings['default_' . $row['variable']] = $row['value'];
1497
		elseif ($row['variable'] == 'theme_dir')
1498
			$theme_paths[$row['id_theme']][$row['variable']] = $row['value'];
1499
	}
1500
	$smcFunc['db_free_result']($request);
1501
1502
	// Are there are mods installed that may need uninstalling?
1503
	$request = $smcFunc['db_query']('', '
1504
		SELECT id_install, filename, name, themes_installed, version
1505
		FROM {db_prefix}log_packages
1506
		WHERE install_state = {int:installed}
1507
		ORDER BY time_installed DESC',
1508
		array(
1509
			'installed' => 1,
1510
			'db_error_skip' => true,
1511
		)
1512
	);
1513
	$upcontext['packages'] = array();
1514
	while ($row = $smcFunc['db_fetch_assoc']($request))
1515
	{
1516
		// Work out the status.
1517
		if (!file_exists($packagesdir . '/' . $row['filename']))
1518
		{
1519
			$status = 'Missing';
1520
			$status_color = 'red';
1521
			$result = 'Removed';
1522
		}
1523
		else
1524
		{
1525
			$status = 'Installed';
1526
			$status_color = 'green';
1527
			$result = 'No Action Needed';
1528
		}
1529
1530
		$upcontext['packages'][$row['id_install']] = array(
1531
			'id' => $row['id_install'],
1532
			'themes' => explode(',', $row['themes_installed']),
1533
			'name' => $row['name'],
1534
			'filename' => $row['filename'],
1535
			'missing_file' => file_exists($packagesdir . '/' . $row['filename']) ? 0 : 1,
1536
			'files' => array(),
1537
			'file_count' => 0,
1538
			'status' => $status,
1539
			'result' => $result,
1540
			'color' => $status_color,
1541
			'version' => $row['version'],
1542
			'needs_removing' => false,
1543
		);
1544
	}
1545
	$smcFunc['db_free_result']($request);
1546
1547
	// Don't carry on if there are none.
1548
	if (empty($upcontext['packages']))
1549
		return true;
1550
1551
	// Setup some basics.
1552
	if (!empty($upcontext['user']['version']))
1553
		$_SESSION['version_emulate'] = $upcontext['user']['version'];
1554
1555
	// Before we get started, don't report notice errors.
1556
	$oldErrorReporting = error_reporting(E_ALL ^ E_NOTICE);
1557
1558
	if (!mktree($packagesdir . '/temp', 0755))
1559
	{
1560
		deltree($packagesdir . '/temp', false);
1561
		if (!mktree($packagesdir . '/temp', 0777))
1562
		{
1563
			deltree($packagesdir . '/temp', false);
1564
			// @todo Error here - plus chmod!
1565
		}
1566
	}
1567
1568
	// Anything which reinstalled should not have its entry removed.
1569
	$reinstall_worked = array();
1570
1571
	// We're gonna be doing some removin'
1572
	$test = isset($_POST['cleandone']) ? false : true;
1573
	foreach ($upcontext['packages'] as $id => $package)
1574
	{
1575
		// Can't do anything about this....
1576
		if ($package['missing_file'])
1577
			continue;
1578
1579
		// Not testing *and* this wasn't checked?
1580
		if (!$test && (!isset($_POST['remove']) || !isset($_POST['remove'][$id])))
1581
			continue;
1582
1583
		// What are the themes this was installed into?
1584
		$cur_theme_paths = array();
1585
		foreach ($theme_paths as $tid => $data)
1586
			if ($tid != 1 && in_array($tid, $package['themes']))
1587
				$cur_theme_paths[$tid] = $data;
1588
1589
		// Get the modifications data if applicable.
1590
		$filename = $package['filename'];
1591
		$packageInfo = getPackageInfo($filename);
1592
		if (!is_array($packageInfo))
1593
			continue;
1594
1595
		$info = parsePackageInfo($packageInfo['xml'], $test, 'uninstall');
1596
		// Also get the reinstall details...
1597
		if (isset($_POST['remove']))
1598
			$infoInstall = parsePackageInfo($packageInfo['xml'], true);
1599
1600 View Code Duplication
		if (is_file($packagesdir . '/' . $filename))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1601
			read_tgz_file($packagesdir . '/' . $filename, $packagesdir . '/temp');
1602
		else
1603
			copytree($packagesdir . '/' . $filename, $packagesdir . '/temp');
1604
1605
		// Work out how we uninstall...
1606
		$files = array();
1607
		foreach ($info as $change)
1608
		{
1609
			// Work out two things:
1610
			// 1) Whether it's installed at the moment - and if so whether its fully installed, and:
1611
			// 2) Whether it could be installed on the new version.
1612
			if ($change['type'] == 'modification')
1613
			{
1614
				$contents = @file_get_contents($packagesdir . '/temp/' . $upcontext['base_path'] . $change['filename']);
1615
				if ($change['boardmod'])
1616
					$results = parseBoardMod($contents, $test, $change['reverse'], $cur_theme_paths);
1617
				else
1618
					$results = parseModification($contents, $test, $change['reverse'], $cur_theme_paths);
1619
1620
				foreach ($results as $action)
1621
				{
1622
					// Something we can remove? Probably means it existed!
1623
					if (($action['type'] == 'replace' || $action['type'] == 'append' || (!empty($action['filename']) && $action['type'] == 'failure')) && !in_array($action['filename'], $files))
1624
						$files[] = $action['filename'];
1625
					if ($action['type'] == 'failure')
1626
					{
1627
						$upcontext['packages'][$id]['needs_removing'] = true;
1628
						$upcontext['packages'][$id]['status'] = 'Reinstall Required';
1629
						$upcontext['packages'][$id]['color'] = '#FD6435';
1630
					}
1631
				}
1632
			}
1633
		}
1634
1635
		// Store this info for the template as appropriate.
1636
		$upcontext['packages'][$id]['files'] = $files;
1637
		$upcontext['packages'][$id]['file_count'] = count($files);
1638
1639
		// If we've done something save the changes!
1640
		if (!$test)
1641
			package_flush_cache();
1642
1643
		// Are we attempting to reinstall this thing?
1644
		if (isset($_POST['remove']) && !$test && isset($infoInstall))
1645
		{
1646
			// Need to extract again I'm afraid.
1647 View Code Duplication
			if (is_file($packagesdir . '/' . $filename))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1648
				read_tgz_file($packagesdir . '/' . $filename, $packagesdir . '/temp');
1649
			else
1650
				copytree($packagesdir . '/' . $filename, $packagesdir . '/temp');
1651
1652
			$errors = false;
1653
			$upcontext['packages'][$id]['result'] = 'Removed';
1654
			foreach ($infoInstall as $change)
1655
			{
1656
				if ($change['type'] == 'modification')
1657
				{
1658
					$contents = @file_get_contents($packagesdir . '/temp/' . $upcontext['base_path'] . $change['filename']);
1659 View Code Duplication
					if ($change['boardmod'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1660
						$results = parseBoardMod($contents, true, $change['reverse'], $cur_theme_paths);
1661
					else
1662
						$results = parseModification($contents, true, $change['reverse'], $cur_theme_paths);
1663
1664
					// Are there any errors?
1665
					foreach ($results as $action)
1666
						if ($action['type'] == 'failure')
1667
							$errors = true;
1668
				}
1669
			}
1670
			if (!$errors)
1671
			{
1672
				$reinstall_worked[] = $id;
1673
				$upcontext['packages'][$id]['result'] = 'Reinstalled';
1674
				$upcontext['packages'][$id]['color'] = 'green';
1675
				foreach ($infoInstall as $change)
1676
				{
1677
					if ($change['type'] == 'modification')
1678
					{
1679
						$contents = @file_get_contents($packagesdir . '/temp/' . $upcontext['base_path'] . $change['filename']);
1680 View Code Duplication
						if ($change['boardmod'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1681
							$results = parseBoardMod($contents, false, $change['reverse'], $cur_theme_paths);
0 ignored issues
show
Unused Code introduced by
$results is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1682
						else
1683
							$results = parseModification($contents, false, $change['reverse'], $cur_theme_paths);
0 ignored issues
show
Unused Code introduced by
$results is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1684
					}
1685
				}
1686
1687
				// Save the changes.
1688
				package_flush_cache();
1689
			}
1690
		}
1691
	}
1692
1693
	// Put errors back on a sec.
1694
	error_reporting($oldErrorReporting);
1695
1696
	// Check everything is writable.
1697
	if ($test && !empty($upcontext['packages']))
1698
	{
1699
		$writable_files = array();
1700
		foreach ($upcontext['packages'] as $package)
1701
		{
1702
			if (!empty($package['files']))
1703
				foreach ($package['files'] as $file)
1704
					$writable_files[] = $file;
1705
		}
1706
1707
		if (!empty($writable_files))
1708
		{
1709
			$writable_files = array_unique($writable_files);
1710
			$upcontext['writable_files'] = $writable_files;
1711
1712
			if (!makeFilesWritable($writable_files))
1713
				return false;
1714
		}
1715
	}
1716
1717
	if (file_exists($packagesdir . '/temp'))
1718
		deltree($packagesdir . '/temp');
1719
1720
	// Removing/Reinstalling any packages?
1721
	if (isset($_POST['remove']))
1722
	{
1723
		$deletes = array();
1724
		foreach ($_POST['remove'] as $id => $dummy)
1725
		{
1726
			if (!in_array((int) $id, $reinstall_worked))
1727
				$deletes[] = (int) $id;
1728
		}
1729
1730
		if (!empty($deletes))
1731
			upgrade_query( '
1732
				UPDATE ' . $db_prefix . 'log_packages
1733
				SET install_state = 0
1734
				WHERE id_install IN (' . implode(',', $deletes) . ')');
1735
1736
		// Ensure we don't lose our changes!
1737
		package_put_contents($packagesdir . '/installed.list', time());
1738
1739
		$upcontext['sub_template'] = 'cleanup_done';
1740
		return false;
1741
	}
1742
	else
1743
	{
1744
		$allgood = true;
1745
		// Is there actually anything that needs our attention?
1746
		foreach ($upcontext['packages'] as $package)
0 ignored issues
show
Bug introduced by
The expression $upcontext['packages'] of type string|boolean|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1747
			if ($package['color'] != 'green')
1748
				$allgood = false;
1749
1750
		if ($allgood)
1751
			return true;
1752
	}
1753
1754
	$_GET['substep'] = 0;
1755
	return isset($_POST['cleandone']) ? true : false;
1756
}
1757
1758
1759
// Delete the damn thing!
1760
function DeleteUpgrade()
1761
{
1762
	global $command_line, $language, $upcontext, $boarddir, $sourcedir, $forum_version, $user_info, $maintenance, $smcFunc, $db_type;
1763
1764
	// Now it's nice to have some of the basic SMF source files.
1765
	if (!isset($_GET['ssi']) && !$command_line)
1766
		redirectLocation('&ssi=1');
1767
1768
	$upcontext['sub_template'] = 'upgrade_complete';
1769
	$upcontext['page_title'] = 'Upgrade Complete';
1770
1771
	$endl = $command_line ? "\n" : '<br>' . "\n";
1772
1773
	$changes = array(
1774
		'language' => '\'' . (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language) . '\'',
1775
		'db_error_send' => '1',
1776
		'upgradeData' => '#remove#',
1777
	);
1778
1779
	// Are we in maintenance mode?
1780
	if (isset($upcontext['user']['main']))
1781
	{
1782
		if ($command_line)
1783
			echo ' * ';
1784
		$upcontext['removed_maintenance'] = true;
1785
		$changes['maintenance'] = $upcontext['user']['main'];
1786
	}
1787
	// Otherwise if somehow we are in 2 let's go to 1.
1788
	elseif (!empty($maintenance) && $maintenance == 2)
1789
		$changes['maintenance'] = 1;
1790
1791
	// Wipe this out...
1792
	$upcontext['user'] = array();
1793
1794
	// Make a backup of Settings.php first as otherwise earlier changes are lost.
1795
	copy($boarddir . '/Settings.php', $boarddir . '/Settings_bak.php');
1796
	changeSettings($changes);
1797
1798
	// Clean any old cache files away.
1799
	clean_cache();
1800
1801
	// Can we delete the file?
1802
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1803
1804
	// Now is the perfect time to fetch the SM files.
1805
	if ($command_line)
1806
		cli_scheduled_fetchSMfiles();
1807
	else
1808
	{
1809
		require_once($sourcedir . '/ScheduledTasks.php');
1810
		$forum_version = SMF_VERSION;  // The variable is usually defined in index.php so lets just use the constant to do it for us.
1811
		scheduled_fetchSMfiles(); // Now go get those files!
1812
	}
1813
1814
	// Log what we've done.
1815
	if (empty($user_info['id']))
1816
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
1817
1818
	// Log the action manually, so CLI still works.
1819
	$smcFunc['db_insert']('',
1820
		'{db_prefix}log_actions',
1821
		array(
1822
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'inet', 'action' => 'string',
1823
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
1824
		),
1825
		array(
1826
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
1827
			0, 0, 0, safe_serialize(array('version' => $forum_version, 'member' => $user_info['id'])),
1828
		),
1829
		array('id_action')
1830
	);
1831
	$user_info['id'] = 0;
1832
1833
	// Save the current database version.
1834
	$server_version = $smcFunc['db_server_info']();
1835 View Code Duplication
	if (($db_type == 'mysql' || $db_type == 'mysqli') && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51')))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1836
		updateSettings(array('db_mysql_group_by_fix' => '1'));
1837
1838
	if ($command_line)
1839
	{
1840
		echo $endl;
1841
		echo 'Upgrade Complete!', $endl;
1842
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
1843
		exit;
1844
	}
1845
1846
	// Make sure it says we're done.
1847
	$upcontext['overall_percent'] = 100;
1848
	if (isset($upcontext['step_progress']))
1849
		unset($upcontext['step_progress']);
1850
1851
	$_GET['substep'] = 0;
1852
	return false;
1853
}
1854
1855
// Just like the built in one, but setup for CLI to not use themes.
1856
function cli_scheduled_fetchSMfiles()
1857
{
1858
	global $sourcedir, $language, $forum_version, $modSettings, $smcFunc;
1859
1860
	if (empty($modSettings['time_format']))
1861
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1862
1863
	// What files do we want to get
1864
	$request = $smcFunc['db_query']('', '
1865
		SELECT id_file, filename, path, parameters
1866
		FROM {db_prefix}admin_info_files',
1867
		array(
1868
		)
1869
	);
1870
1871
	$js_files = array();
1872 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1873
	{
1874
		$js_files[$row['id_file']] = array(
1875
			'filename' => $row['filename'],
1876
			'path' => $row['path'],
1877
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode($forum_version)),
1878
		);
1879
	}
1880
	$smcFunc['db_free_result']($request);
1881
1882
	// We're gonna need fetch_web_data() to pull this off.
1883
	require_once($sourcedir . '/Subs-Package.php');
1884
1885
	foreach ($js_files as $ID_FILE => $file)
1886
	{
1887
		// Create the url
1888
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'http://www.simplemachines.org' : '';
1889
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1890
1891
		// Get the file
1892
		$file_data = fetch_web_data($url);
1893
1894
		// If we got an error - give up - the site might be down.
1895
		if ($file_data === false)
1896
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
1897
1898
		// Save the file to the database.
1899
		$smcFunc['db_query']('substring', '
1900
			UPDATE {db_prefix}admin_info_files
1901
			SET data = SUBSTRING({string:file_data}, 1, 65534)
1902
			WHERE id_file = {int:id_file}',
1903
			array(
1904
				'id_file' => $ID_FILE,
1905
				'file_data' => $file_data,
1906
			)
1907
		);
1908
	}
1909
	return true;
1910
}
1911
1912
function convertSettingsToTheme()
1913
{
1914
	global $db_prefix, $modSettings, $smcFunc;
1915
1916
	$values = array(
1917
		'show_latest_member' => @$GLOBALS['showlatestmember'],
1918
		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
1919
		'show_modify' => @$GLOBALS['showmodify'],
1920
		'show_user_images' => @$GLOBALS['showuserpic'],
1921
		'show_blurb' => @$GLOBALS['showusertext'],
1922
		'show_gender' => @$GLOBALS['showgenderimage'],
1923
		'show_newsfader' => @$GLOBALS['shownewsfader'],
1924
		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
1925
		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
1926
		'linktree_link' => @$GLOBALS['curposlinks'],
1927
		'show_profile_buttons' => @$GLOBALS['profilebutton'],
1928
		'show_mark_read' => @$GLOBALS['showmarkread'],
1929
		'newsfader_time' => @$GLOBALS['fadertime'],
1930
		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
1931
		'enable_news' => @$GLOBALS['enable_news'],
1932
		'return_to_post' => @$modSettings['returnToPost'],
1933
	);
1934
1935
	$themeData = array();
1936
	foreach ($values as $variable => $value)
1937
	{
1938
		if (!isset($value) || $value === null)
1939
			$value = 0;
1940
1941
		$themeData[] = array(0, 1, $variable, $value);
1942
	}
1943
	if (!empty($themeData))
1944
	{
1945
		$smcFunc['db_insert']('ignore',
1946
			$db_prefix . 'themes',
1947
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
1948
			$themeData,
1949
			array('id_member', 'id_theme', 'variable')
1950
		);
1951
	}
1952
}
1953
1954
// This function only works with MySQL but that's fine as it is only used for v1.0.
1955
function convertSettingstoOptions()
1956
{
1957
	global $modSettings, $smcFunc;
1958
1959
	// Format: new_setting -> old_setting_name.
1960
	$values = array(
1961
		'calendar_start_day' => 'cal_startmonday',
1962
		'view_newest_first' => 'viewNewestFirst',
1963
		'view_newest_pm_first' => 'viewNewestFirst',
1964
	);
1965
1966
	foreach ($values as $variable => $value)
1967
	{
1968
		if (empty($modSettings[$value[0]]))
1969
			continue;
1970
1971
		$smcFunc['db_query']('', '
1972
			INSERT IGNORE INTO {db_prefix}themes
1973
				(id_member, id_theme, variable, value)
1974
			SELECT id_member, 1, {string:variable}, {string:value}
1975
			FROM {db_prefix}members',
1976
			array(
1977
				'variable' => $variable,
1978
				'value' => $modSettings[$value[0]],
1979
				'db_error_skip' => true,
1980
			)
1981
		);
1982
1983
		$smcFunc['db_query']('', '
1984
			INSERT IGNORE INTO {db_prefix}themes
1985
				(id_member, id_theme, variable, value)
1986
			VALUES (-1, 1, {string:variable}, {string:value})',
1987
			array(
1988
				'variable' => $variable,
1989
				'value' => $modSettings[$value[0]],
1990
				'db_error_skip' => true,
1991
			)
1992
		);
1993
	}
1994
}
1995
1996
function changeSettings($config_vars)
1997
{
1998
	global $boarddir;
1999
2000
	$settingsArray = file($boarddir . '/Settings_bak.php');
2001
2002
	if (count($settingsArray) == 1)
2003
		$settingsArray = preg_split('~[\r\n]~', $settingsArray[0]);
2004
2005
	for ($i = 0, $n = count($settingsArray); $i < $n; $i++)
2006
	{
2007
		// Don't trim or bother with it if it's not a variable.
2008
		if (substr($settingsArray[$i], 0, 1) == '$')
2009
		{
2010
			$settingsArray[$i] = trim($settingsArray[$i]) . "\n";
2011
2012
			foreach ($config_vars as $var => $val)
2013
			{
2014
				if (isset($settingsArray[$i]) && strncasecmp($settingsArray[$i], '$' . $var, 1 + strlen($var)) == 0)
2015
				{
2016 View Code Duplication
					if ($val == '#remove#')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2017
						unset($settingsArray[$i]);
2018
					else
2019
					{
2020
						$comment = strstr(substr($settingsArray[$i], strpos($settingsArray[$i], ';')), '#');
2021
						$settingsArray[$i] = '$' . $var . ' = ' . $val . ';' . ($comment != '' ? "\t\t" . $comment : "\n");
2022
					}
2023
2024
					unset($config_vars[$var]);
2025
				}
2026
			}
2027
		}
2028 View Code Duplication
		if (isset($settingsArray[$i]))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2029
		{
2030
			if (trim(substr($settingsArray[$i], 0, 2)) == '?' . '>')
2031
				$end = $i;
2032
		}
2033
	}
2034
2035
	// Assume end-of-file if the end wasn't found.
2036
	if (empty($end) || $end < 10)
2037
		$end = count($settingsArray);
2038
2039 View Code Duplication
	if (!empty($config_vars))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2040
	{
2041
		$settingsArray[$end++] = '';
2042
		foreach ($config_vars as $var => $val)
2043
		{
2044
			if ($val != '#remove#')
2045
				$settingsArray[$end++] = '$' . $var . ' = ' . $val . ';' . "\n";
2046
		}
2047
	}
2048
	// This should be the last line and even last bytes of the file.
2049
	$settingsArray[$end] = '?' . '>';
2050
2051
	// Blank out the file - done to fix a oddity with some servers.
2052
	$fp = fopen($boarddir . '/Settings.php', 'w');
2053
	fclose($fp);
2054
2055
	$fp = fopen($boarddir . '/Settings.php', 'r+');
2056
	for ($i = 0; $i < $end; $i++)
2057
	{
2058
		if (isset($settingsArray[$i]))
2059
			fwrite($fp, strtr($settingsArray[$i], "\r", ''));
2060
	}
2061
	fwrite($fp, rtrim($settingsArray[$i]));
2062
	fclose($fp);
2063
}
2064
function updateLastError()
2065
{
2066
	// clear out the db_last_error file
2067
	file_put_contents(dirname(__FILE__) . '/db_last_error.php', '<' . '?' . "php\n" . '$db_last_error = 0;' . "\n" . '?' . '>');
2068
}
2069
2070
function php_version_check()
2071
{
2072
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
2073
}
2074
2075
function db_version_check()
2076
{
2077
	global $db_type, $databases;
2078
2079
	$curver = eval($databases[$db_type]['version_check']);
2080
	$curver = preg_replace('~\-.+?$~', '', $curver);
2081
2082
	return version_compare($databases[$db_type]['version'], $curver, '<=');
2083
}
2084
2085
function getMemberGroups()
2086
{
2087
	global $smcFunc;
2088
	static $member_groups = array();
2089
2090
	if (!empty($member_groups))
2091
		return $member_groups;
2092
2093
	$request = $smcFunc['db_query']('', '
2094
		SELECT group_name, id_group
2095
		FROM {db_prefix}membergroups
2096
		WHERE id_group = {int:admin_group} OR id_group > {int:old_group}',
2097
		array(
2098
			'admin_group' => 1,
2099
			'old_group' => 7,
2100
			'db_error_skip' => true,
2101
		)
2102
	);
2103
	if ($request === false)
2104
	{
2105
		$request = $smcFunc['db_query']('', '
2106
			SELECT membergroup, id_group
2107
			FROM {db_prefix}membergroups
2108
			WHERE id_group = {int:admin_group} OR id_group > {int:old_group}',
2109
			array(
2110
				'admin_group' => 1,
2111
				'old_group' => 7,
2112
				'db_error_skip' => true,
2113
			)
2114
		);
2115
	}
2116
	while ($row = $smcFunc['db_fetch_row']($request))
2117
		$member_groups[trim($row[0])] = $row[1];
2118
	$smcFunc['db_free_result']($request);
2119
2120
	return $member_groups;
2121
}
2122
2123
function fixRelativePath($path)
2124
{
2125
	global $install_path;
2126
2127
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
2128
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
2129
}
2130
2131
function parse_sql($filename)
2132
{
2133
	global $db_prefix, $db_collation, $boarddir, $boardurl, $command_line, $file_steps, $step_progress, $custom_warning;
2134
	global $upcontext, $support_js, $is_debug, $smcFunc, $databases, $db_type, $db_character_set;
2135
2136
/*
2137
	Failure allowed on:
2138
		- INSERT INTO but not INSERT IGNORE INTO.
2139
		- UPDATE IGNORE but not UPDATE.
2140
		- ALTER TABLE and ALTER IGNORE TABLE.
2141
		- DROP TABLE.
2142
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
2143
2144
	If a comment...
2145
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
2146
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
2147
		- is only ---#, it is "done." and then a break - only shown in debug.
2148
		- begins with ---{ it is a code block terminating at ---}.
2149
2150
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
2151
2152
	Replaces the following variables:
2153
		- {$boarddir}
2154
		- {$boardurl}
2155
		- {$db_prefix}
2156
		- {$db_collation}
2157
*/
2158
2159
	// May want to use extended functionality.
2160
	db_extend();
2161
	db_extend('packages');
2162
2163
	// Our custom error handler - does nothing but does stop public errors from XML!
2164
	if (!function_exists('sql_error_handler'))
2165
	{
2166
		function sql_error_handler($errno, $errstr, $errfile, $errline)
0 ignored issues
show
Unused Code introduced by
The parameter $errno is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2167
		{
2168
			global $support_js;
2169
2170
			if ($support_js)
2171
				return true;
2172
			else
2173
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
2174
		}
2175
	}
2176
2177
	// Make our own error handler.
2178
	set_error_handler('sql_error_handler');
2179
2180
	// 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!
2181
	if (!empty($databases[$db_type]['utf8_support']) && version_compare($databases[$db_type]['utf8_version'], eval($databases[$db_type]['utf8_version_check']), '>'))
2182
	{
2183
		$request = $smcFunc['db_query']('', '
2184
			SHOW TABLE STATUS
2185
			LIKE {string:table_name}',
2186
			array(
2187
				'table_name' => "{$db_prefix}members",
2188
				'db_error_skip' => true,
2189
			)
2190
		);
2191
		if ($smcFunc['db_num_rows']($request) === 0)
2192
			die('Unable to find members table!');
2193
		$table_status = $smcFunc['db_fetch_assoc']($request);
2194
		$smcFunc['db_free_result']($request);
2195
2196
		if (!empty($table_status['Collation']))
2197
		{
2198
			$request = $smcFunc['db_query']('', '
2199
				SHOW COLLATION
2200
				LIKE {string:collation}',
2201
				array(
2202
					'collation' => $table_status['Collation'],
2203
					'db_error_skip' => true,
2204
				)
2205
			);
2206
			// Got something?
2207
			if ($smcFunc['db_num_rows']($request) !== 0)
2208
				$collation_info = $smcFunc['db_fetch_assoc']($request);
2209
			$smcFunc['db_free_result']($request);
2210
2211
			// Excellent!
2212
			if (!empty($collation_info['Collation']) && !empty($collation_info['Charset']))
2213
				$db_collation = ' CHARACTER SET ' . $collation_info['Charset'] . ' COLLATE ' . $collation_info['Collation'];
2214
		}
2215
	}
2216
	if (empty($db_collation))
2217
		$db_collation = '';
2218
2219
	$endl = $command_line ? "\n" : '<br>' . "\n";
2220
2221
	$lines = file($filename);
2222
2223
	$current_type = 'sql';
2224
	$current_data = '';
2225
	$substep = 0;
2226
	$last_step = '';
2227
2228
	// Make sure all newly created tables will have the proper characters set.
2229
	if (isset($db_character_set) && $db_character_set === 'utf8')
2230
		$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
2231
2232
	// Count the total number of steps within this file - for progress.
2233
	$file_steps = substr_count(implode('', $lines), '---#');
2234
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
2235
	$upcontext['debug_items'] = $file_steps;
2236
	$upcontext['current_item_num'] = 0;
2237
	$upcontext['current_item_name'] = '';
2238
	$upcontext['current_debug_item_num'] = 0;
2239
	$upcontext['current_debug_item_name'] = '';
2240
	// This array keeps a record of what we've done in case java is dead...
2241
	$upcontext['actioned_items'] = array();
2242
2243
	$done_something = false;
2244
2245
	foreach ($lines as $line_number => $line)
2246
	{
2247
		$do_current = $substep >= $_GET['substep'];
2248
2249
		// Get rid of any comments in the beginning of the line...
2250
		if (substr(trim($line), 0, 2) === '/*')
2251
			$line = preg_replace('~/\*.+?\*/~', '', $line);
2252
2253
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
2254
		if ($is_debug && !$support_js && $command_line)
2255
			flush();
2256
2257
		if (trim($line) === '')
2258
			continue;
2259
2260
		if (trim(substr($line, 0, 3)) === '---')
2261
		{
2262
			$type = substr($line, 3, 1);
2263
2264
			// An error??
2265
			if (trim($current_data) != '' && $type !== '}')
2266
			{
2267
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
2268
				if ($command_line)
2269
					echo $upcontext['error_message'];
2270
			}
2271
2272
			if ($type == ' ')
2273
			{
2274
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
2275
				{
2276
					echo ' Successful.', $endl;
2277
					flush();
2278
				}
2279
2280
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
2281
				$upcontext['current_item_num']++;
2282
				$upcontext['current_item_name'] = $last_step;
2283
2284
				if ($do_current)
2285
				{
2286
					$upcontext['actioned_items'][] = $last_step;
2287
					if ($command_line)
2288
						echo ' * ';
2289
				}
2290
			}
2291
			elseif ($type == '#')
2292
			{
2293
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
2294
2295
				$upcontext['current_debug_item_num']++;
2296
				if (trim($line) != '---#')
2297
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
2298
2299
				// Have we already done something?
2300
				if (isset($_GET['xml']) && $done_something)
2301
				{
2302
					restore_error_handler();
2303
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
2304
				}
2305
2306
				if ($do_current)
2307
				{
2308
					if (trim($line) == '---#' && $command_line)
2309
						echo ' done.', $endl;
2310
					elseif ($command_line)
2311
						echo ' +++ ', rtrim(substr($line, 4));
2312
					elseif (trim($line) != '---#')
2313
					{
2314
						if ($is_debug)
2315
							$upcontext['actioned_items'][] = htmlspecialchars(rtrim(substr($line, 4)));
2316
					}
2317
				}
2318
2319
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
2320
				{
2321
					if ($command_line)
2322
						echo ' * ';
2323
					else
2324
						$upcontext['actioned_items'][] = $last_step;
2325
				}
2326
2327
				// Small step - only if we're actually doing stuff.
2328
				if ($do_current)
2329
					nextSubstep(++$substep);
2330
				else
2331
					$substep++;
2332
			}
2333
			elseif ($type == '{')
2334
				$current_type = 'code';
2335
			elseif ($type == '}')
2336
			{
2337
				$current_type = 'sql';
2338
2339
				if (!$do_current)
2340
				{
2341
					$current_data = '';
2342
					continue;
2343
				}
2344
2345
				if (eval('global $db_prefix, $modSettings, $smcFunc; ' . $current_data) === false)
2346
				{
2347
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
2348
					if ($command_line)
2349
						echo $upcontext['error_message'];
2350
				}
2351
2352
				// Done with code!
2353
				$current_data = '';
2354
				$done_something = true;
2355
			}
2356
2357
			continue;
2358
		}
2359
2360
		$current_data .= $line;
2361
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
2362
		{
2363
			if ((!$support_js || isset($_GET['xml'])))
2364
			{
2365
				if (!$do_current)
2366
				{
2367
					$current_data = '';
2368
					continue;
2369
				}
2370
2371
				$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));
2372
2373
				upgrade_query($current_data);
2374
2375
				// @todo This will be how it kinda does it once mysql all stripped out - needed for postgre (etc).
2376
				/*
2377
				$result = $smcFunc['db_query']('', $current_data, false, false);
2378
				// Went wrong?
2379
				if (!$result)
2380
				{
2381
					// Bit of a bodge - do we want the error?
2382
					if (!empty($upcontext['return_error']))
2383
					{
2384
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
2385
						return false;
2386
					}
2387
				}*/
2388
				$done_something = true;
2389
			}
2390
			$current_data = '';
2391
		}
2392
		// If this is xml based and we're just getting the item name then that's grand.
2393
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
2394
		{
2395
			restore_error_handler();
2396
			return false;
2397
		}
2398
2399
		// Clean up by cleaning any step info.
2400
		$step_progress = array();
2401
		$custom_warning = '';
2402
	}
2403
2404
	// Put back the error handler.
2405
	restore_error_handler();
2406
2407
	if ($command_line)
2408
	{
2409
		echo ' Successful.' . "\n";
2410
		flush();
2411
	}
2412
2413
	$_GET['substep'] = 0;
2414
	return true;
2415
}
2416
2417
function upgrade_query($string, $unbuffered = false)
2418
{
2419
	global $db_connection, $db_server, $db_user, $db_passwd, $db_type, $command_line, $upcontext, $upgradeurl, $modSettings;
2420
	global $db_name, $db_unbuffered, $smcFunc;
2421
2422
	// Get the query result - working around some SMF specific security - just this once!
2423
	$modSettings['disableQueryCheck'] = true;
2424
	$db_unbuffered = $unbuffered;
2425
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
2426
	$db_unbuffered = false;
2427
2428
	// Failure?!
2429
	if ($result !== false)
2430
		return $result;
2431
2432
	$db_error_message = $smcFunc['db_error']($db_connection);
2433
	// If MySQL we do something more clever.
2434
	if ($db_type == 'mysql' || $db_type == 'mysqli')
2435
	{
2436
		$mysql_errno = ($db_type == 'mysqli') ? mysqli_errno($db_connection) : mysql_errno($db_connection);
2437
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR'));
2438
2439
		// Error numbers:
2440
		//    1016: Can't open file '....MYI'
2441
		//    1050: Table already exists.
2442
		//    1054: Unknown column name.
2443
		//    1060: Duplicate column name.
2444
		//    1061: Duplicate key name.
2445
		//    1062: Duplicate entry for unique key.
2446
		//    1068: Multiple primary keys.
2447
		//    1072: Key column '%s' doesn't exist in table.
2448
		//    1091: Can't drop key, doesn't exist.
2449
		//    1146: Table doesn't exist.
2450
		//    2013: Lost connection to server during query.
2451
2452
		if ($mysql_errno == 1016)
2453
		{
2454
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
2455
			{
2456
				if ($db_type == 'mysql')
2457
				{
2458
					mysql_query('REPAIR TABLE `' . $match[1] . '`');
2459
					$result = mysql_query($string);
2460
				}
2461
				else
2462
				{
2463
					mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
2464
					$result = mysqli_query($db_connection, $string);
2465
				}
2466
				if ($result !== false)
2467
					return $result;
2468
			}
2469
		}
2470
		elseif ($mysql_errno == 2013)
2471
		{
2472
			$db_connection = mysql_connect($db_server, $db_user, $db_passwd);
2473
			if ($db_type == 'mysql')
2474
			{
2475
				mysql_select_db($db_name, $db_connection);
2476
				if ($db_connection)
2477
				{
2478
					$result = mysql_query($string);
2479
					if ($result !== false)
2480
						return $result;
2481
				}
2482
			}
2483
			else
2484
			{
2485
				mysqli_select_db($db_connection, $db_name);
2486
				if ($db_connection)
2487
				{
2488
					$result = mysqli_query($db_connection, $string);
2489
					if ($result !== false)
2490
						return $result;
2491
				}
2492
			}
2493
		}
2494
		// Duplicate column name... should be okay ;).
2495 View Code Duplication
		elseif (in_array($mysql_errno, array(1060, 1061, 1068, 1091)))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

Loading history...
2499
			return false;
2500
		// Creating an index on a non-existent column.
2501
		elseif ($mysql_errno == 1072)
2502
			return false;
2503
		elseif ($mysql_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
2504
			return false;
2505
	}
2506
	// If a table already exists don't go potty.
2507
	else
2508
	{
2509
		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U')))
2510
		{
2511
			if (strpos($db_error_message, 'exist') !== false)
2512
				return true;
2513
		}
2514
		elseif (strpos(trim($string), 'INSERT ') !== false)
2515
		{
2516
			if (strpos($db_error_message, 'duplicate') !== false)
2517
				return true;
2518
		}
2519
	}
2520
2521
	// Get the query string so we pass everything.
2522
	$query_string = '';
2523
	foreach ($_GET as $k => $v)
2524
		$query_string .= ';' . $k . '=' . $v;
2525
	if (strlen($query_string) != 0)
2526
		$query_string = '?' . substr($query_string, 1);
2527
2528
	if ($command_line)
2529
	{
2530
		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
2531
		die;
2532
	}
2533
2534
	// Bit of a bodge - do we want the error?
2535
	if (!empty($upcontext['return_error']))
2536
	{
2537
		$upcontext['error_message'] = $db_error_message;
2538
		$upcontext['error_string'] = $string;
2539
		return false;
2540
	}
2541
2542
	// Otherwise we have to display this somewhere appropriate if possible.
2543
	$upcontext['forced_error_message'] = '
2544
			<strong>Unsuccessful!</strong><br>
2545
2546
			<div style="margin: 2ex;">
2547
				This query:
2548
				<blockquote><pre>' . nl2br(htmlspecialchars(trim($string))) . ';</pre></blockquote>
2549
2550
				Caused the error:
2551
				<blockquote>' . nl2br(htmlspecialchars($db_error_message)) . '</blockquote>
2552
			</div>
2553
2554
			<form action="' . $upgradeurl . $query_string . '" method="post">
2555
				<input type="submit" value="Try again" class="button_submit">
2556
			</form>
2557
		</div>';
2558
2559
	upgradeExit();
2560
}
2561
2562
function smf_mysql_fetch_assoc($rs)
2563
{
2564
	global $db_type;
2565
	return ($db_type == 'mysql') ? mysql_fetch_assoc($rs) : mysqli_fetch_assoc($rs);
2566
}
2567
2568
function smf_mysql_fetch_row($rs)
2569
{
2570
	global $db_type;
2571
	return ($db_type == 'mysql') ? mysql_fetch_row($rs) : mysqli_fetch_row($rs);
2572
}
2573
2574
function smf_mysql_free_result($rs)
2575
{
2576
	global $db_type;
2577
	return ($db_type == 'mysql') ? mysql_free_result($rs) : mysqli_free_result($rs);
2578
}
2579
2580
function smf_mysql_insert_id($rs)
2581
{
2582
	global $db_type;
2583
	return ($db_type == 'mysql') ? mysql_insert_id($rs) : mysqli_insert_id($rs);
2584
}
2585
2586
function smf_mysql_num_rows($rs)
2587
{
2588
	global $db_type;
2589
	return ($db_type == 'mysql') ? mysql_num_rows($rs) : mysqli_num_rows($rs);
2590
}
2591
2592
function smf_mysql_real_escape_string($string)
2593
{
2594
	global $db_type, $db_connection;
2595
	return ($db_type == 'mysql') ? mysql_real_escape_string($string, $db_connection) : mysqli_real_escape_string($db_connection, $string);
2596
}
2597
2598
// This performs a table alter, but does it unbuffered so the script can time out professionally.
2599
function protected_alter($change, $substep, $is_test = false)
2600
{
2601
	global $db_prefix, $smcFunc;
2602
2603
	db_extend('packages');
2604
2605
	// Firstly, check whether the current index/column exists.
2606
	$found = false;
2607
	if ($change['type'] === 'column')
2608
	{
2609
		$columns = $smcFunc['db_list_columns']('{db_prefix}' . $change['table'], true);
2610
		foreach ($columns as $column)
2611
		{
2612
			// Found it?
2613
			if ($column['name'] === $change['name'])
2614
			{
2615
				$found |= 1;
2616
				// Do some checks on the data if we have it set.
2617
				if (isset($change['col_type']))
2618
					$found &= $change['col_type'] === $column['type'];
2619
				if (isset($change['null_allowed']))
2620
					$found &= $column['null'] == $change['null_allowed'];
2621
				if (isset($change['default']))
2622
					$found &= $change['default'] === $column['default'];
2623
			}
2624
		}
2625
	}
2626
	elseif ($change['type'] === 'index')
2627
	{
2628
		$request = upgrade_query( '
2629
			SHOW INDEX
2630
			FROM ' . $db_prefix . $change['table']);
2631
		if ($request !== false)
2632
		{
2633
			$cur_index = array();
2634
2635
			while ($row = $smcFunc['db_fetch_assoc']($request))
2636
				if ($row['Key_name'] === $change['name'])
2637
					$cur_index[(int) $row['Seq_in_index']] = $row['Column_name'];
2638
2639
			ksort($cur_index, SORT_NUMERIC);
2640
			$found = array_values($cur_index) === $change['target_columns'];
2641
2642
			$smcFunc['db_free_result']($request);
2643
		}
2644
	}
2645
2646
	// If we're trying to add and it's added, we're done.
2647
	if ($found && in_array($change['method'], array('add', 'change')))
2648
		return true;
2649
	// Otherwise if we're removing and it wasn't found we're also done.
2650
	elseif (!$found && in_array($change['method'], array('remove', 'change_remove')))
2651
		return true;
2652
	// Otherwise is it just a test?
2653
	elseif ($is_test)
2654
		return false;
2655
2656
	// Not found it yet? Bummer! How about we see if we're currently doing it?
2657
	$running = false;
2658
	$found = false;
2659
	while (1 == 1)
2660
	{
2661
		$request = upgrade_query('
2662
			SHOW FULL PROCESSLIST');
2663
		while ($row = $smcFunc['db_fetch_assoc']($request))
2664
		{
2665
			if (strpos($row['Info'], 'ALTER TABLE ' . $db_prefix . $change['table']) !== false && strpos($row['Info'], $change['text']) !== false)
2666
				$found = true;
2667
		}
2668
2669
		// Can't find it? Then we need to run it fools!
2670
		if (!$found && !$running)
2671
		{
2672
			$smcFunc['db_free_result']($request);
2673
2674
			$success = upgrade_query('
2675
				ALTER TABLE ' . $db_prefix . $change['table'] . '
2676
				' . $change['text'], true) !== false;
2677
2678
			if (!$success)
2679
				return false;
2680
2681
			// Return
2682
			$running = true;
2683
		}
2684
		// What if we've not found it, but we'd ran it already? Must of completed.
2685
		elseif (!$found)
2686
		{
2687
			$smcFunc['db_free_result']($request);
2688
			return true;
2689
		}
2690
2691
		// Pause execution for a sec or three.
2692
		sleep(3);
2693
2694
		// Can never be too well protected.
2695
		nextSubstep($substep);
2696
	}
2697
2698
	// Protect it.
2699
	nextSubstep($substep);
2700
}
2701
2702
// Alter a text column definition preserving its character set.
2703
function textfield_alter($change, $substep)
2704
{
2705
	global $db_prefix, $databases, $db_type, $smcFunc;
2706
2707
	// Versions of MySQL < 4.1 wouldn't benefit from character set detection.
2708
	if (empty($databases[$db_type]['utf8_support']) || version_compare($databases[$db_type]['utf8_version'], eval($databases[$db_type]['utf8_version_check']), '>'))
2709
	{
2710
		$column_fix = true;
2711
		$null_fix = !$change['null_allowed'];
2712
	}
2713
	else
2714
	{
2715
		$request = $smcFunc['db_query']('', '
2716
			SHOW FULL COLUMNS
2717
			FROM {db_prefix}' . $change['table'] . '
2718
			LIKE {string:column}',
2719
			array(
2720
				'column' => $change['column'],
2721
				'db_error_skip' => true,
2722
			)
2723
		);
2724
		if ($smcFunc['db_num_rows']($request) === 0)
2725
			die('Unable to find column ' . $change['column'] . ' inside table ' . $db_prefix . $change['table']);
2726
		$table_row = $smcFunc['db_fetch_assoc']($request);
2727
		$smcFunc['db_free_result']($request);
2728
2729
		// If something of the current column definition is different, fix it.
2730
		$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']);
2731
2732
		// Columns that previously allowed null, need to be converted first.
2733
		$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
2734
2735
		// Get the character set that goes with the collation of the column.
2736 View Code Duplication
		if ($column_fix && !empty($table_row['Collation']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2737
		{
2738
			$request = $smcFunc['db_query']('', '
2739
				SHOW COLLATION
2740
				LIKE {string:collation}',
2741
				array(
2742
					'collation' => $table_row['Collation'],
2743
					'db_error_skip' => true,
2744
				)
2745
			);
2746
			// No results? Just forget it all together.
2747
			if ($smcFunc['db_num_rows']($request) === 0)
2748
				unset($table_row['Collation']);
2749
			else
2750
				$collation_info = $smcFunc['db_fetch_assoc']($request);
2751
			$smcFunc['db_free_result']($request);
2752
		}
2753
	}
2754
2755
	if ($column_fix)
2756
	{
2757
		// Make sure there are no NULL's left.
2758
		if ($null_fix)
2759
			$smcFunc['db_query']('', '
2760
				UPDATE {db_prefix}' . $change['table'] . '
2761
				SET ' . $change['column'] . ' = {string:default}
2762
				WHERE ' . $change['column'] . ' IS NULL',
2763
				array(
2764
					'default' => isset($change['default']) ? $change['default'] : '',
2765
					'db_error_skip' => true,
2766
				)
2767
			);
2768
2769
		// Do the actual alteration.
2770
		$smcFunc['db_query']('', '
2771
			ALTER TABLE {db_prefix}' . $change['table'] . '
2772
			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}' : ''),
2773
			array(
2774
				'default' => isset($change['default']) ? $change['default'] : '',
2775
				'db_error_skip' => true,
2776
			)
2777
		);
2778
	}
2779
	nextSubstep($substep);
2780
}
2781
2782
// Check if we need to alter this query.
2783
function checkChange(&$change)
2784
{
2785
	global $smcFunc, $db_type, $databases;
2786
	static $database_version, $where_field_support;
2787
2788
	// Attempt to find a database_version.
2789
	if (empty($database_version))
2790
	{
2791
		$database_version = $databases[$db_type]['version_check'];
2792
		$where_field_support = ($db_type == 'mysql' || $db_type == 'mysqli') && version_compare('5.0', $database_version, '<=');
2793
	}
2794
2795
	// Not a column we need to check on?
2796
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
2797
		return;
2798
2799
	// Break it up you (six|seven).
2800
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
2801
2802
	// Can we support a shortcut method?
2803
	if ($where_field_support)
2804
	{
2805
		// Get the details about this change.
2806
		$request = $smcFunc['db_query']('', '
2807
			SHOW FIELDS
2808
			FROM {db_prefix}{raw:table}
2809
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
2810
			array(
2811
				'table' => $change['table'],
2812
				'old_name' => $temp[1],
2813
				'new_name' => $temp[2],
2814
		));
2815
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2816
		if ($smcFunc['db_num_rows'] != 1)
2817
			return;
2818
2819
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
2820
		$smcFunc['db_free_result']($request);
2821
	}
2822
	else
2823
	{
2824
		// Do this the old fashion, sure method way.
2825
		$request = $smcFunc['db_query']('', '
2826
			SHOW FIELDS
2827
			FROM {db_prefix}{raw:table}',
2828
			array(
2829
				'table' => $change['table'],
2830
		));
2831
		// Mayday!
2832
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2833
		if ($smcFunc['db_num_rows'] == 0)
2834
			return;
2835
2836
		// Oh where, oh where has my little field gone. Oh where can it be...
2837
		while ($row = $smcFunc['db_query']($request))
2838
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
2839
			{
2840
				$current_type = $row['Type'];
2841
				break;
2842
			}
2843
	}
2844
2845
	// If this doesn't match, the column may of been altered for a reason.
2846
	if (trim($current_type) != trim($temp[3]))
2847
		$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...
2848
2849
	// Piece this back together.
2850
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
2851
}
2852
2853
// The next substep.
2854
function nextSubstep($substep)
2855
{
2856
	global $start_time, $timeLimitThreshold, $command_line, $custom_warning;
2857
	global $step_progress, $is_debug, $upcontext;
2858
2859
	if ($_GET['substep'] < $substep)
2860
		$_GET['substep'] = $substep;
2861
2862
	if ($command_line)
2863
	{
2864
		if (time() - $start_time > 1 && empty($is_debug))
2865
		{
2866
			echo '.';
2867
			$start_time = time();
2868
		}
2869
		return;
2870
	}
2871
2872
	@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...
2873
	if (function_exists('apache_reset_timeout'))
2874
		@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...
2875
2876
	if (time() - $start_time <= $timeLimitThreshold)
2877
		return;
2878
2879
	// Do we have some custom step progress stuff?
2880
	if (!empty($step_progress))
2881
	{
2882
		$upcontext['substep_progress'] = 0;
2883
		$upcontext['substep_progress_name'] = $step_progress['name'];
2884
		if ($step_progress['current'] > $step_progress['total'])
2885
			$upcontext['substep_progress'] = 99.9;
2886
		else
2887
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2888
2889
		// Make it nicely rounded.
2890
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2891
	}
2892
2893
	// If this is XML we just exit right away!
2894
	if (isset($_GET['xml']))
2895
		return upgradeExit();
2896
2897
	// We're going to pause after this!
2898
	$upcontext['pause'] = true;
2899
2900
	$upcontext['query_string'] = '';
2901
	foreach ($_GET as $k => $v)
2902
	{
2903
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2904
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2905
	}
2906
2907
	// Custom warning?
2908
	if (!empty($custom_warning))
2909
		$upcontext['custom_warning'] = $custom_warning;
2910
2911
	upgradeExit();
2912
}
2913
2914
function cmdStep0()
2915
{
2916
	global $boarddir, $sourcedir, $language, $modSettings, $start_time, $cachedir, $databases, $db_type, $smcFunc, $upcontext;
2917
	global $language, $is_debug;
2918
	$start_time = time();
2919
2920
	ob_end_clean();
2921
	ob_implicit_flush(true);
2922
	@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...
2923
2924
	if (!isset($_SERVER['argv']))
2925
		$_SERVER['argv'] = array();
2926
	$_GET['maint'] = 1;
2927
2928
	foreach ($_SERVER['argv'] as $i => $arg)
2929
	{
2930
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2931
			$_GET['lang'] = $match[1];
2932
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2933
			continue;
2934
		elseif ($arg == '--no-maintenance')
2935
			$_GET['maint'] = 0;
2936
		elseif ($arg == '--debug')
2937
			$is_debug = true;
2938
		elseif ($arg == '--backup')
2939
			$_POST['backup'] = 1;
2940
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2941
			$_GET['conv'] = 1;
2942
		elseif ($i != 0)
2943
		{
2944
			echo 'SMF Command-line Upgrader
2945
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2946
2947
    --language=LANG         Reset the forum\'s language to LANG.
2948
    --no-maintenance        Don\'t put the forum into maintenance mode.
2949
    --debug                 Output debugging information.
2950
    --backup                Create backups of tables with "backup_" prefix.';
2951
			echo "\n";
2952
			exit;
2953
		}
2954
	}
2955
2956
	if (!php_version_check())
2957
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
2958
	if (!db_version_check())
2959
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2960
2961
	// Do some checks to make sure they have proper privileges
2962
	db_extend('packages');
2963
2964
	// CREATE
2965
	$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');
2966
2967
	// ALTER
2968
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'tinytext', 'null' => false, 'default' => ''));
2969
2970
	// DROP
2971
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2972
2973
	// Sorry... we need CREATE, ALTER and DROP
2974 View Code Duplication
	if (!$create || !$alter || !$drop)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2975
		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);
2976
2977
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2978
		&& @file_exists($sourcedir . '/QueryString.php')
2979
		&& @file_exists($sourcedir . '/ManageBoards.php');
2980
	if (!$check && !isset($modSettings['smfVersion']))
2981
		print_error('Error: Some files are missing or out-of-date.', true);
2982
2983
	// Do a quick version spot check.
2984
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
2985
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2986
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2987
		print_error('Error: Some files have not yet been updated properly.');
2988
2989
	// Make sure Settings.php is writable.
2990
		quickFileWritable($boarddir . '/Settings.php');
2991
	if (!is_writable($boarddir . '/Settings.php'))
2992
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2993
2994
	// Make sure Settings_bak.php is writable.
2995
		quickFileWritable($boarddir . '/Settings_bak.php');
2996
	if (!is_writable($boarddir . '/Settings_bak.php'))
2997
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2998
2999 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3000
		print_error('Error: Unable to obtain write access to "agreement.txt".');
3001
	elseif (isset($modSettings['agreement']))
3002
	{
3003
		$fp = fopen($boarddir . '/agreement.txt', 'w');
3004
		fwrite($fp, $modSettings['agreement']);
3005
		fclose($fp);
3006
	}
3007
3008
	// Make sure Themes is writable.
3009
	quickFileWritable($modSettings['theme_dir']);
3010
3011
	if (!is_writable($modSettings['theme_dir']) && !isset($modSettings['smfVersion']))
3012
		print_error('Error: Unable to obtain write access to "Themes".');
3013
3014
	// Make sure cache directory exists and is writable!
3015
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
3016
	if (!file_exists($cachedir_temp))
3017
		@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...
3018
3019
	// Make sure the cache temp dir is writable.
3020
	quickFileWritable($cachedir_temp);
3021
3022
	if (!is_writable($cachedir_temp))
3023
		print_error('Error: Unable to obtain write access to "cache".', true);
3024
3025
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
3026
		print_error('Error: Unable to find language files!', true);
3027
	else
3028
	{
3029
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
3030
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
3031
3032
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
3033
			print_error('Error: Language files out of date.', true);
3034
		if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
3035
			print_error('Error: Install language is missing for selected language.', true);
3036
3037
		// Otherwise include it!
3038
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
3039
	}
3040
3041
	// Make sure we skip the HTML for login.
3042
	$_POST['upcont'] = true;
3043
	$upcontext['current_step'] = 1;
3044
}
3045
3046
function print_error($message, $fatal = false)
3047
{
3048
	static $fp = null;
3049
3050
	if ($fp === null)
3051
		$fp = fopen('php://stderr', 'wb');
3052
3053
	fwrite($fp, $message . "\n");
3054
3055
	if ($fatal)
3056
		exit;
3057
}
3058
3059
function throw_error($message)
3060
{
3061
	global $upcontext;
3062
3063
	$upcontext['error_msg'] = $message;
3064
	$upcontext['sub_template'] = 'error_message';
3065
3066
	return false;
3067
}
3068
3069
// Check files are writable - make them writable if necessary...
3070
function makeFilesWritable(&$files)
3071
{
3072
	global $upcontext, $boarddir;
3073
3074
	if (empty($files))
3075
		return true;
3076
3077
	$failure = false;
3078
	// On linux, it's easy - just use is_writable!
3079
	if (substr(__FILE__, 1, 2) != ':\\')
3080
	{
3081
		$upcontext['systemos'] = 'linux';
3082
3083
		foreach ($files as $k => $file)
3084
		{
3085
			if (!is_writable($file))
3086
			{
3087
				@chmod($file, 0755);
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...
3088
3089
				// Well, 755 hopefully worked... if not, try 777.
3090
				if (!is_writable($file) && !@chmod($file, 0777))
3091
					$failure = true;
3092
				// Otherwise remove it as it's good!
3093
				else
3094
					unset($files[$k]);
3095
			}
3096
			else
3097
				unset($files[$k]);
3098
		}
3099
	}
3100
	// Windows is trickier.  Let's try opening for r+...
3101
	else
3102
	{
3103
		$upcontext['systemos'] = 'windows';
3104
3105
		foreach ($files as $k => $file)
3106
		{
3107
			// Folders can't be opened for write... but the index.php in them can ;).
3108
			if (is_dir($file))
3109
				$file .= '/index.php';
3110
3111
			// Funny enough, chmod actually does do something on windows - it removes the read only attribute.
3112
			@chmod($file, 0777);
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...
3113
			$fp = @fopen($file, 'r+');
3114
3115
			// Hmm, okay, try just for write in that case...
3116
			if (!$fp)
3117
				$fp = @fopen($file, 'w');
3118
3119
			if (!$fp)
3120
				$failure = true;
3121
			else
3122
				unset($files[$k]);
3123
			@fclose($fp);
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...
3124
		}
3125
	}
3126
3127
	if (empty($files))
3128
		return true;
3129
3130
	if (!isset($_SERVER))
3131
		return !$failure;
3132
3133
	// What still needs to be done?
3134
	$upcontext['chmod']['files'] = $files;
3135
3136
	// If it's windows it's a mess...
3137
	if ($failure && substr(__FILE__, 1, 2) == ':\\')
3138
	{
3139
		$upcontext['chmod']['ftp_error'] = 'total_mess';
3140
3141
		return false;
3142
	}
3143
	// We're going to have to use... FTP!
3144
	elseif ($failure)
3145
	{
3146
		// Load any session data we might have...
3147
		if (!isset($_POST['ftp_username']) && isset($_SESSION['installer_temp_ftp']))
3148
		{
3149
			$upcontext['chmod']['server'] = $_SESSION['installer_temp_ftp']['server'];
3150
			$upcontext['chmod']['port'] = $_SESSION['installer_temp_ftp']['port'];
3151
			$upcontext['chmod']['username'] = $_SESSION['installer_temp_ftp']['username'];
3152
			$upcontext['chmod']['password'] = $_SESSION['installer_temp_ftp']['password'];
3153
			$upcontext['chmod']['path'] = $_SESSION['installer_temp_ftp']['path'];
3154
		}
3155
		// Or have we submitted?
3156 View Code Duplication
		elseif (isset($_POST['ftp_username']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3157
		{
3158
			$upcontext['chmod']['server'] = $_POST['ftp_server'];
3159
			$upcontext['chmod']['port'] = $_POST['ftp_port'];
3160
			$upcontext['chmod']['username'] = $_POST['ftp_username'];
3161
			$upcontext['chmod']['password'] = $_POST['ftp_password'];
3162
			$upcontext['chmod']['path'] = $_POST['ftp_path'];
3163
		}
3164
3165
		if (isset($upcontext['chmod']['username']))
3166
		{
3167
			$ftp = new ftp_connection($upcontext['chmod']['server'], $upcontext['chmod']['port'], $upcontext['chmod']['username'], $upcontext['chmod']['password']);
3168
3169
			if ($ftp->error === false)
3170
			{
3171
				// Try it without /home/abc just in case they messed up.
3172
				if (!$ftp->chdir($upcontext['chmod']['path']))
3173
				{
3174
					$upcontext['chmod']['ftp_error'] = $ftp->last_message;
3175
					$ftp->chdir(preg_replace('~^/home[2]?/[^/]+?~', '', $upcontext['chmod']['path']));
3176
				}
3177
			}
3178
		}
3179
3180
		if (!isset($ftp) || $ftp->error !== false)
3181
		{
3182
			if (!isset($ftp))
3183
				$ftp = new ftp_connection(null);
3184
			// Save the error so we can mess with listing...
3185
			elseif ($ftp->error !== false && !isset($upcontext['chmod']['ftp_error']))
3186
				$upcontext['chmod']['ftp_error'] = $ftp->last_message === null ? '' : $ftp->last_message;
3187
3188
			list ($username, $detect_path, $found_path) = $ftp->detect_path(dirname(__FILE__));
3189
3190
			if ($found_path || !isset($upcontext['chmod']['path']))
3191
				$upcontext['chmod']['path'] = $detect_path;
3192
3193
			if (!isset($upcontext['chmod']['username']))
3194
				$upcontext['chmod']['username'] = $username;
3195
3196
			// Don't forget the login token.
3197
			$upcontext += createToken('login');
3198
3199
			return false;
3200
		}
3201
		else
3202
		{
3203
			// We want to do a relative path for FTP.
3204
			if (!in_array($upcontext['chmod']['path'], array('', '/')))
3205
			{
3206
				$ftp_root = strtr($boarddir, array($upcontext['chmod']['path'] => ''));
3207
				if (substr($ftp_root, -1) == '/' && ($upcontext['chmod']['path'] == '' || $upcontext['chmod']['path'][0] === '/'))
3208
				$ftp_root = substr($ftp_root, 0, -1);
3209
			}
3210
			else
3211
				$ftp_root = $boarddir;
3212
3213
			// Save the info for next time!
3214
			$_SESSION['installer_temp_ftp'] = array(
3215
				'server' => $upcontext['chmod']['server'],
3216
				'port' => $upcontext['chmod']['port'],
3217
				'username' => $upcontext['chmod']['username'],
3218
				'password' => $upcontext['chmod']['password'],
3219
				'path' => $upcontext['chmod']['path'],
3220
				'root' => $ftp_root,
3221
			);
3222
3223
			foreach ($files as $k => $file)
3224
			{
3225
				if (!is_writable($file))
3226
					$ftp->chmod($file, 0755);
3227
				if (!is_writable($file))
3228
					$ftp->chmod($file, 0777);
3229
3230
				// Assuming that didn't work calculate the path without the boarddir.
3231
				if (!is_writable($file))
3232
				{
3233
					if (strpos($file, $boarddir) === 0)
3234
					{
3235
						$ftp_file = strtr($file, array($_SESSION['installer_temp_ftp']['root'] => ''));
3236
						$ftp->chmod($ftp_file, 0755);
3237
						if (!is_writable($file))
3238
							$ftp->chmod($ftp_file, 0777);
3239
						// Sometimes an extra slash can help...
3240
						$ftp_file = '/' . $ftp_file;
3241
						if (!is_writable($file))
3242
							$ftp->chmod($ftp_file, 0755);
3243
						if (!is_writable($file))
3244
							$ftp->chmod($ftp_file, 0777);
3245
					}
3246
				}
3247
3248
				if (is_writable($file))
3249
					unset($files[$k]);
3250
			}
3251
3252
			$ftp->close();
3253
		}
3254
	}
3255
3256
	// What remains?
3257
	$upcontext['chmod']['files'] = $files;
3258
3259
	if (empty($files))
3260
		return true;
3261
3262
	return false;
3263
}
3264
3265
function quickFileWritable($file)
3266
{
3267
	if (is_writable($file))
3268
		return true;
3269
3270
	@chmod($file, 0755);
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...
3271
3272
	// Try 755 and 775 first since 777 doesn't always work and could be a risk...
3273
	$chmod_values = array(0755, 0775, 0777);
3274
3275
	foreach($chmod_values as $val)
3276
	{
3277
		// If it's writable, break out of the loop
3278
		if (is_writable($file))
3279
			break;
3280
		else
3281
			@chmod($file, $val);
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...
3282
	}
3283
}
3284
function smf_strtolower($string)
3285
{
3286
	global $sourcedir;
3287
	if (function_exists('mb_strtolower'))
3288
		return mb_strtolower($string, 'UTF-8');
3289
	require_once($sourcedir . '/Subs-Charset.php');
3290
	return utf8_strtolower($string);
3291
}
3292
3293
/**
3294
 * Handles converting your database to UTF-8
3295
 */
3296
function convertUtf8()
3297
{
3298
	global $upcontext, $db_character_set, $sourcedir, $smcFunc, $modSettings, $language, $db_prefix, $db_type, $command_line, $support_js, $is_debug;
3299
3300
	// First make sure they aren't already on UTF-8 before we go anywhere...
3301
	if ($db_type == 'postgresql' || ($db_character_set === 'utf8' && !empty($modSettings['global_character_set']) && $modSettings['global_character_set'] === 'UTF-8'))
3302
	{
3303
		$smcFunc['db_insert']('replace',
3304
			'{db_prefix}settings',
3305
			array('variable' => 'string', 'value' => 'string'),
3306
			array(array('global_character_set', 'UTF-8')),
3307
			array('variable')
3308
		);
3309
		
3310
		return true;
3311
	}
3312
	else
3313
	{
3314
		$upcontext['page_title'] = 'Converting to UTF8';
3315
		$upcontext['sub_template'] = isset($_GET['xml']) ? 'convert_xml' : 'convert_utf8';
3316
3317
		// The character sets used in SMF's language files with their db equivalent.
3318
		$charsets = array(
3319
			// Armenian
3320
			'armscii8' => 'armscii8',
3321
			// Chinese-traditional.
3322
			'big5' => 'big5',
3323
			// Chinese-simplified.
3324
			'gbk' => 'gbk',
3325
			// West European.
3326
			'ISO-8859-1' => 'latin1',
3327
			// Romanian.
3328
			'ISO-8859-2' => 'latin2',
3329
			// Turkish.
3330
			'ISO-8859-9' => 'latin5',
3331
			// Latvian
3332
			'ISO-8859-13' => 'latin7',
3333
			// West European with Euro sign.
3334
			'ISO-8859-15' => 'latin9',
3335
			// Thai.
3336
			'tis-620' => 'tis620',
3337
			// Persian, Chinese, etc.
3338
			'UTF-8' => 'utf8',
3339
			// Russian.
3340
			'windows-1251' => 'cp1251',
3341
			// Greek.
3342
			'windows-1253' => 'utf8',
3343
			// Hebrew.
3344
			'windows-1255' => 'utf8',
3345
			// Arabic.
3346
			'windows-1256' => 'cp1256',
3347
		);
3348
3349
		// Get a list of character sets supported by your MySQL server.
3350
		$request = $smcFunc['db_query']('', '
3351
			SHOW CHARACTER SET',
3352
			array(
3353
			)
3354
		);
3355
		$db_charsets = array();
3356
		while ($row = $smcFunc['db_fetch_assoc']($request))
3357
			$db_charsets[] = $row['Charset'];
3358
3359
		$smcFunc['db_free_result']($request);
3360
3361
		// Character sets supported by both MySQL and SMF's language files.
3362
		$charsets = array_intersect($charsets, $db_charsets);
3363
3364
		// Use the messages.body column as indicator for the database charset.
3365
		$request = $smcFunc['db_query']('', '
3366
			SHOW FULL COLUMNS
3367
			FROM {db_prefix}messages
3368
			LIKE {string:body_like}',
3369
			array(
3370
				'body_like' => 'body',
3371
			)
3372
		);
3373
		$column_info = $smcFunc['db_fetch_assoc']($request);
3374
		$smcFunc['db_free_result']($request);
3375
3376
		// A collation looks like latin1_swedish. We only need the character set.
3377
		list($upcontext['database_charset']) = explode('_', $column_info['Collation']);
3378
		$upcontext['database_charset'] = in_array($upcontext['database_charset'], $charsets) ? array_search($upcontext['database_charset'], $charsets) : $upcontext['database_charset'];
3379
3380
		// Detect whether a fulltext index is set.
3381
		$request = $smcFunc['db_query']('', '
3382
 			SHOW INDEX
3383
	  	    FROM {db_prefix}messages',
3384
			array(
3385
			)
3386
		);
3387
3388
		$upcontext['dropping_index'] = false;
3389
3390
		// If there's a fulltext index, we need to drop it first...
3391 View Code Duplication
		if ($request !== false || $smcFunc['db_num_rows']($request) != 0)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3392
		{
3393
			while ($row = $smcFunc['db_fetch_assoc']($request))
3394
				if ($row['Column_name'] == 'body' && (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT' || isset($row['Comment']) && $row['Comment'] == 'FULLTEXT'))
3395
					$upcontext['fulltext_index'][] = $row['Key_name'];
3396
			$smcFunc['db_free_result']($request);
3397
3398
			if (isset($upcontext['fulltext_index']))
3399
				$upcontext['fulltext_index'] = array_unique($upcontext['fulltext_index']);
3400
		}
3401
3402
		// Drop it and make a note...
3403
		if (!empty($upcontext['fulltext_index']))
3404
		{
3405
			$upcontext['dropping_index'] = true;
3406
3407
			$smcFunc['db_query']('', '
3408
  			ALTER TABLE {db_prefix}messages
3409
	  		DROP INDEX ' . implode(',
3410
		  	DROP INDEX ', $upcontext['fulltext_index']),
3411
				array(
3412
					'db_error_skip' => true,
3413
				)
3414
			);
3415
3416
			// Update the settings table
3417
			$smcFunc['db_insert']('replace',
3418
				'{db_prefix}settings',
3419
				array('variable' => 'string', 'value' => 'string'),
3420
				array('db_search_index', ''),
3421
				array('variable')
3422
			);
3423
		}
3424
3425
		// Figure out what charset we should be converting from...
3426
		$lang_charsets = array(
3427
			'arabic' => 'windows-1256',
3428
			'armenian_east' => 'armscii-8',
3429
			'armenian_west' => 'armscii-8',
3430
			'azerbaijani_latin' => 'ISO-8859-9',
3431
			'bangla' => 'UTF-8',
3432
			'belarusian' => 'ISO-8859-5',
3433
			'bulgarian' => 'windows-1251',
3434
			'cambodian' => 'UTF-8',
3435
			'chinese_simplified' => 'gbk',
3436
			'chinese_traditional' => 'big5',
3437
			'croation' => 'ISO-8859-2',
3438
			'czech' => 'ISO-8859-2',
3439
			'czech_informal' => 'ISO-8859-2',
3440
			'english_pirate' => 'UTF-8',
3441
			'esperanto' => 'ISO-8859-3',
3442
			'estonian' => 'ISO-8859-15',
3443
			'filipino_tagalog' => 'UTF-8',
3444
			'filipino_vasayan' => 'UTF-8',
3445
			'georgian' => 'UTF-8',
3446
			'greek' => 'ISO-8859-3',
3447
			'hebrew' => 'windows-1255',
3448
			'hungarian' => 'ISO-8859-2',
3449
			'irish' => 'UTF-8',
3450
			'japanese' => 'UTF-8',
3451
			'khmer' => 'UTF-8',
3452
			'korean' => 'UTF-8',
3453
			'kurdish_kurmanji' => 'ISO-8859-9',
3454
			'kurdish_sorani' => 'windows-1256',
3455
			'lao' => 'tis-620',
3456
			'latvian' => 'ISO-8859-13',
3457
			'lithuanian' => 'ISO-8859-4',
3458
			'macedonian' => 'UTF-8',
3459
			'malayalam' => 'UTF-8',
3460
			'mongolian' => 'UTF-8',
3461
			'nepali' => 'UTF-8',
3462
			'persian' => 'UTF-8',
3463
			'polish' => 'ISO-8859-2',
3464
			'romanian' => 'ISO-8859-2',
3465
			'russian' => 'windows-1252',
3466
			'sakha' => 'UTF-8',
3467
			'serbian_cyrillic' => 'ISO-8859-5',
3468
			'serbian_latin' => 'ISO-8859-2',
3469
			'sinhala' => 'UTF-8',
3470
			'slovak' => 'ISO-8859-2',
3471
			'slovenian' => 'ISO-8859-2',
3472
			'telugu' => 'UTF-8',
3473
			'thai' => 'tis-620',
3474
			'turkish' => 'ISO-8859-9',
3475
			'turkmen' => 'ISO-8859-9',
3476
			'ukranian' => 'windows-1251',
3477
			'urdu' => 'UTF-8',
3478
			'uzbek_cyrillic' => 'ISO-8859-5',
3479
			'uzbek_latin' => 'ISO-8859-5',
3480
			'vietnamese' => 'UTF-8',
3481
			'yoruba' => 'UTF-8'
3482
		);
3483
3484
		// Default to ISO-8859-1 unless we detected another supported charset
3485
		$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';
3486
3487
		$upcontext['charset_list'] = array_keys($charsets);
3488
3489
		// Translation table for the character sets not native for MySQL.
3490
		$translation_tables = array(
3491
			'windows-1255' => array(
3492
				'0x81' => '\'\'',		'0x8A' => '\'\'',		'0x8C' => '\'\'',
3493
				'0x8D' => '\'\'',		'0x8E' => '\'\'',		'0x8F' => '\'\'',
3494
				'0x90' => '\'\'',		'0x9A' => '\'\'',		'0x9C' => '\'\'',
3495
				'0x9D' => '\'\'',		'0x9E' => '\'\'',		'0x9F' => '\'\'',
3496
				'0xCA' => '\'\'',		'0xD9' => '\'\'',		'0xDA' => '\'\'',
3497
				'0xDB' => '\'\'',		'0xDC' => '\'\'',		'0xDD' => '\'\'',
3498
				'0xDE' => '\'\'',		'0xDF' => '\'\'',		'0xFB' => '\'\'',
3499
				'0xFC' => '\'\'',		'0xFF' => '\'\'',		'0xC2' => '0xFF',
3500
				'0x80' => '0xFC',		'0xE2' => '0xFB',		'0xA0' => '0xC2A0',
3501
				'0xA1' => '0xC2A1',		'0xA2' => '0xC2A2',		'0xA3' => '0xC2A3',
3502
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
3503
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
3504
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
3505
				'0xAF' => '0xC2AF',		'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',
3506
				'0xB2' => '0xC2B2',		'0xB3' => '0xC2B3',		'0xB4' => '0xC2B4',
3507
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
3508
				'0xB8' => '0xC2B8',		'0xB9' => '0xC2B9',		'0xBB' => '0xC2BB',
3509
				'0xBC' => '0xC2BC',		'0xBD' => '0xC2BD',		'0xBE' => '0xC2BE',
3510
				'0xBF' => '0xC2BF',		'0xD7' => '0xD7B3',		'0xD1' => '0xD781',
3511
				'0xD4' => '0xD7B0',		'0xD5' => '0xD7B1',		'0xD6' => '0xD7B2',
3512
				'0xE0' => '0xD790',		'0xEA' => '0xD79A',		'0xEC' => '0xD79C',
3513
				'0xED' => '0xD79D',		'0xEE' => '0xD79E',		'0xEF' => '0xD79F',
3514
				'0xF0' => '0xD7A0',		'0xF1' => '0xD7A1',		'0xF2' => '0xD7A2',
3515
				'0xF3' => '0xD7A3',		'0xF5' => '0xD7A5',		'0xF6' => '0xD7A6',
3516
				'0xF7' => '0xD7A7',		'0xF8' => '0xD7A8',		'0xF9' => '0xD7A9',
3517
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
3518
				'0x86' => '0xE280A0',	'0x87' => '0xE280A1',	'0x89' => '0xE280B0',
3519
				'0x8B' => '0xE280B9',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
3520
				'0x95' => '0xE280A2',	'0x97' => '0xE28094',	'0x99' => '0xE284A2',
3521
				'0xC0' => '0xD6B0',		'0xC1' => '0xD6B1',		'0xC3' => '0xD6B3',
3522
				'0xC4' => '0xD6B4',		'0xC5' => '0xD6B5',		'0xC6' => '0xD6B6',
3523
				'0xC7' => '0xD6B7',		'0xC8' => '0xD6B8',		'0xC9' => '0xD6B9',
3524
				'0xCB' => '0xD6BB',		'0xCC' => '0xD6BC',		'0xCD' => '0xD6BD',
3525
				'0xCE' => '0xD6BE',		'0xCF' => '0xD6BF',		'0xD0' => '0xD780',
3526
				'0xD2' => '0xD782',		'0xE3' => '0xD793',		'0xE4' => '0xD794',
3527
				'0xE5' => '0xD795',		'0xE7' => '0xD797',		'0xE9' => '0xD799',
3528
				'0xFD' => '0xE2808E',	'0xFE' => '0xE2808F',	'0x92' => '0xE28099',
3529
				'0x83' => '0xC692',		'0xD3' => '0xD783',		'0x88' => '0xCB86',
3530
				'0x98' => '0xCB9C',		'0x91' => '0xE28098',	'0x96' => '0xE28093',
3531
				'0xBA' => '0xC3B7',		'0x9B' => '0xE280BA',	'0xAA' => '0xC397',
3532
				'0xA4' => '0xE282AA',	'0xE1' => '0xD791',		'0xE6' => '0xD796',
3533
				'0xE8' => '0xD798',		'0xEB' => '0xD79B',		'0xF4' => '0xD7A4',
3534
				'0xFA' => '0xD7AA',		'0xFF' => '0xD6B2',		'0xFC' => '0xE282AC',
3535
				'0xFB' => '0xD792',
3536
			),
3537
			'windows-1253' => array(
3538
				'0x81' => '\'\'',			'0x88' => '\'\'',			'0x8A' => '\'\'',
3539
				'0x8C' => '\'\'',			'0x8D' => '\'\'',			'0x8E' => '\'\'',
3540
				'0x8F' => '\'\'',			'0x90' => '\'\'',			'0x98' => '\'\'',
3541
				'0x9A' => '\'\'',			'0x9C' => '\'\'',			'0x9D' => '\'\'',
3542
				'0x9E' => '\'\'',			'0x9F' => '\'\'',			'0xAA' => '\'\'',
3543
				'0xD2' => '\'\'',			'0xFF' => '\'\'',			'0xCE' => '0xCE9E',
3544
				'0xB8' => '0xCE88',		'0xBA' => '0xCE8A',		'0xBC' => '0xCE8C',
3545
				'0xBE' => '0xCE8E',		'0xBF' => '0xCE8F',		'0xC0' => '0xCE90',
3546
				'0xC8' => '0xCE98',		'0xCA' => '0xCE9A',		'0xCC' => '0xCE9C',
3547
				'0xCD' => '0xCE9D',		'0xCF' => '0xCE9F',		'0xDA' => '0xCEAA',
3548
				'0xE8' => '0xCEB8',		'0xEA' => '0xCEBA',		'0xEC' => '0xCEBC',
3549
				'0xEE' => '0xCEBE',		'0xEF' => '0xCEBF',		'0xC2' => '0xFF',
3550
				'0xBD' => '0xC2BD',		'0xED' => '0xCEBD',		'0xB2' => '0xC2B2',
3551
				'0xA0' => '0xC2A0',		'0xA3' => '0xC2A3',		'0xA4' => '0xC2A4',
3552
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
3553
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
3554
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
3555
				'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',		'0xB3' => '0xC2B3',
3556
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
3557
				'0xBB' => '0xC2BB',		'0xE2' => '0xCEB2',		'0x80' => '0xD2',
3558
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
3559
				'0x86' => '0xE280A0',	'0xA1' => '0xCE85',		'0xA2' => '0xCE86',
3560
				'0x87' => '0xE280A1',	'0x89' => '0xE280B0',	'0xB9' => '0xCE89',
3561
				'0x8B' => '0xE280B9',	'0x91' => '0xE28098',	'0x99' => '0xE284A2',
3562
				'0x92' => '0xE28099',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
3563
				'0x95' => '0xE280A2',	'0x96' => '0xE28093',	'0x97' => '0xE28094',
3564
				'0x9B' => '0xE280BA',	'0xAF' => '0xE28095',	'0xB4' => '0xCE84',
3565
				'0xC1' => '0xCE91',		'0xC3' => '0xCE93',		'0xC4' => '0xCE94',
3566
				'0xC5' => '0xCE95',		'0xC6' => '0xCE96',		'0x83' => '0xC692',
3567
				'0xC7' => '0xCE97',		'0xC9' => '0xCE99',		'0xCB' => '0xCE9B',
3568
				'0xD0' => '0xCEA0',		'0xD1' => '0xCEA1',		'0xD3' => '0xCEA3',
3569
				'0xD4' => '0xCEA4',		'0xD5' => '0xCEA5',		'0xD6' => '0xCEA6',
3570
				'0xD7' => '0xCEA7',		'0xD8' => '0xCEA8',		'0xD9' => '0xCEA9',
3571
				'0xDB' => '0xCEAB',		'0xDC' => '0xCEAC',		'0xDD' => '0xCEAD',
3572
				'0xDE' => '0xCEAE',		'0xDF' => '0xCEAF',		'0xE0' => '0xCEB0',
3573
				'0xE1' => '0xCEB1',		'0xE3' => '0xCEB3',		'0xE4' => '0xCEB4',
3574
				'0xE5' => '0xCEB5',		'0xE6' => '0xCEB6',		'0xE7' => '0xCEB7',
3575
				'0xE9' => '0xCEB9',		'0xEB' => '0xCEBB',		'0xF0' => '0xCF80',
3576
				'0xF1' => '0xCF81',		'0xF2' => '0xCF82',		'0xF3' => '0xCF83',
3577
				'0xF4' => '0xCF84',		'0xF5' => '0xCF85',		'0xF6' => '0xCF86',
3578
				'0xF7' => '0xCF87',		'0xF8' => '0xCF88',		'0xF9' => '0xCF89',
3579
				'0xFA' => '0xCF8A',		'0xFB' => '0xCF8B',		'0xFC' => '0xCF8C',
3580
				'0xFD' => '0xCF8D',		'0xFE' => '0xCF8E',		'0xFF' => '0xCE92',
3581
				'0xD2' => '0xE282AC',
3582
			),
3583
		);
3584
3585
		// Make some preparations.
3586
		if (isset($translation_tables[$upcontext['charset_detected']]))
3587
		{
3588
			$replace = '%field%';
3589
3590
			// Build a huge REPLACE statement...
3591
			foreach ($translation_tables[$upcontext['charset_detected']] as $from => $to)
3592
				$replace = 'REPLACE(' . $replace . ', ' . $from . ', ' . $to . ')';
3593
		}
3594
3595
		// Get a list of table names ahead of time... This makes it easier to set our substep and such
3596
		db_extend();
3597
		$queryTables = $smcFunc['db_list_tables'](false, $db_prefix . '%');
3598
3599
		$upcontext['table_count'] = count($queryTables);
3600
		$file_steps = $upcontext['table_count'];
0 ignored issues
show
Unused Code introduced by
$file_steps is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
3601
3602
		for($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
3603
		{
3604
			$table = $queryTables[$_GET['substep']];
3605
3606
			// Do we need to pause?
3607
			nextSubstep($substep);
3608
3609
			$getTableStatus = $smcFunc['db_query']('', '
3610
				SHOW TABLE STATUS
3611
				LIKE {string:table_name}',
3612
				array(
3613
					'table_name' => str_replace('_', '\_', $table)
3614
				)
3615
			);
3616
3617
			// Only one row so we can just fetch_assoc and free the result...
3618
			$table_info = $smcFunc['db_fetch_assoc']($getTableStatus);
3619
			$smcFunc['db_free_result']($getTableStatus);
3620
3621
			$upcontext['cur_table_num'] = $_GET['substep'];
3622
			$upcontext['cur_table_name'] = $table_info['Name'];
3623
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3624
3625
			// Just to make sure it doesn't time out.
3626
			if (function_exists('apache_reset_timeout'))
3627
				@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...
3628
3629
			$table_charsets = array();
3630
3631
			// Loop through each column.
3632
			$queryColumns = $smcFunc['db_query']('', '
3633
				SHOW FULL COLUMNS
3634
				FROM ' . $table_info['Name'],
3635
				array(
3636
				)
3637
			);
3638
			while ($column_info = $smcFunc['db_fetch_assoc']($queryColumns))
3639
			{
3640
				// Only text'ish columns have a character set and need converting.
3641
				if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false)
3642
				{
3643
					$collation = empty($column_info['Collation']) || $column_info['Collation'] === 'NULL' ? $table_info['Collation'] : $column_info['Collation'];
3644
					if (!empty($collation) && $collation !== 'NULL')
3645
					{
3646
						list($charset) = explode('_', $collation);
3647
3648
						if (!isset($table_charsets[$charset]))
3649
							$table_charsets[$charset] = array();
3650
3651
						$table_charsets[$charset][] = $column_info;
3652
					}
3653
				}
3654
			}
3655
			$smcFunc['db_free_result']($queryColumns);
3656
3657
			// Only change the column if the data doesn't match the current charset.
3658
			if ((count($table_charsets) === 1 && key($table_charsets) !== $charsets[$upcontext['charset_detected']]) || count($table_charsets) > 1)
3659
			{
3660
				$updates_blob = '';
3661
				$updates_text = '';
3662
				foreach ($table_charsets as $charset => $columns)
3663
				{
3664
					if ($charset !== $charsets[$upcontext['charset_detected']])
3665
					{
3666
						foreach ($columns as $column)
3667
						{
3668
							$updates_blob .= '
3669
								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'] . '\'') . ',';
3670
							$updates_text .= '
3671
								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'] . '\'') . ',';
3672
						}
3673
					}
3674
				}
3675
3676
				// Change the columns to binary form.
3677
				$smcFunc['db_query']('', '
3678
					ALTER TABLE {raw:table_name}{raw:updates_blob}',
3679
					array(
3680
						'table_name' => $table_info['Name'],
3681
						'updates_blob' => substr($updates_blob, 0, -1),
3682
					)
3683
				);
3684
3685
				// Convert the character set if MySQL has no native support for it.
3686
				if (isset($translation_tables[$upcontext['charset_detected']]))
3687
				{
3688
					$update = '';
3689
					foreach ($table_charsets as $charset => $columns)
3690
						foreach ($columns as $column)
3691
							$update .= '
3692
								' . $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...
3693
3694
					$smcFunc['db_query']('', '
3695
						UPDATE {raw:table_name}
3696
						SET {raw:updates}',
3697
						array(
3698
							'table_name' => $table_info['Name'],
3699
							'updates' => substr($update, 0, -1),
3700
						)
3701
					);
3702
				}
3703
3704
				// Change the columns back, but with the proper character set.
3705
				$smcFunc['db_query']('', '
3706
					ALTER TABLE {raw:table_name}{raw:updates_text}',
3707
					array(
3708
						'table_name' => $table_info['Name'],
3709
						'updates_text' => substr($updates_text, 0, -1),
3710
					)
3711
				);
3712
			}
3713
3714
			// Now do the actual conversion (if still needed).
3715
			if ($charsets[$upcontext['charset_detected']] !== 'utf8')
3716
			{
3717
				if ($command_line)
3718
					echo 'Converting table ' . $table_info['Name'] . ' to UTF-8...';
3719
3720
				$smcFunc['db_query']('', '
3721
					ALTER TABLE {raw:table_name}
3722
					CONVERT TO CHARACTER SET utf8',
3723
						array(
3724
								'table_name' => $table_info['Name'],
3725
						)
3726
				);
3727
3728
				if ($command_line)
3729
					echo " done.\n";
3730
			}
3731
		}
3732
3733
		$prev_charset = empty($translation_tables[$upcontext['charset_detected']]) ? $charsets[$upcontext['charset_detected']] : $translation_tables[$upcontext['charset_detected']];
3734
3735
		$smcFunc['db_insert']('replace',
3736
			'{db_prefix}settings',
3737
			array('variable' => 'string', 'value' => 'string'),
3738
			array(array('global_character_set', 'UTF-8'), array('previousCharacterSet', $prev_charset)),
3739
			array('variable')
3740
		);
3741
3742
		// Store it in Settings.php too because it's needed before db connection.
3743
		// Hopefully this works...
3744
		require_once($sourcedir . '/Subs-Admin.php');
3745
		updateSettingsFile(array('db_character_set' => '\'utf8\''));
3746
3747
		// The conversion might have messed up some serialized strings. Fix them!
3748
		$request = $smcFunc['db_query']('', '
3749
			SELECT id_action, extra
3750
			FROM {db_prefix}log_actions
3751
			WHERE action IN ({string:remove}, {string:delete})',
3752
			array(
3753
				'remove' => 'remove',
3754
				'delete' => 'delete',
3755
			)
3756
		);
3757 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3758
		{
3759
			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)
3760
				$smcFunc['db_query']('', '
3761
					UPDATE {db_prefix}log_actions
3762
					SET extra = {string:extra}
3763
					WHERE id_action = {int:current_action}',
3764
					array(
3765
						'current_action' => $row['id_action'],
3766
						'extra' => $matches[1] . strlen($matches[3]) . ':"' . $matches[3] . '"' . $matches[4],
3767
					)
3768
				);
3769
		}
3770
		$smcFunc['db_free_result']($request);
3771
3772
		if ($upcontext['dropping_index'] && $command_line)
3773
		{
3774
			echo "\nYour fulltext search index was dropped to facilitate the conversion. You will need to recreate it.";
3775
			flush();
3776
		}
3777
	}
3778
3779
	return true;
3780
}
3781
3782
function serialize_to_json()
3783
{
3784
	global $command_line, $smcFunc, $modSettings, $sourcedir, $upcontext, $support_js, $is_debug;
3785
3786
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'serialize_json_xml' : 'serialize_json';
3787
	// First thing's first - did we already do this?
3788
	if (!empty($modSettings['json_done']))
3789
	{
3790
		if ($command_line)
3791
			return DeleteUpgrade();
3792
		else
3793
			return true;
3794
	}
3795
3796
	// Done it already - js wise?
3797
	if (!empty($_POST['json_done']))
3798
		return true;
3799
3800
	// List of tables affected by this function
3801
	// name => array('key', col1[,col2|true[,col3]])
3802
	// If 3rd item in array is true, it indicates that col1 could be empty...
3803
	$tables = array(
3804
		'background_tasks' => array('id_task', 'task_data'),
3805
		'log_actions' => array('id_action', 'extra'),
3806
		'log_online' => array('session', 'url'),
3807
		'log_packages' => array('id_install', 'db_changes', 'failed_steps', 'credits'),
3808
		'log_spider_hits' => array('id_hit', 'url'),
3809
		'log_subscribed' => array('id_sublog', 'pending_details'),
3810
		'pm_rules' => array('id_rule', 'criteria', 'actions'),
3811
		'qanda' => array('id_question', 'answers'),
3812
		'subscriptions' => array('id_subscribe', 'cost'),
3813
		'user_alerts' => array('id_alert', 'extra', true),
3814
		'user_drafts' => array('id_draft', 'to_list', true),
3815
		// These last two are a bit different - we'll handle those separately
3816
		'settings' => array(),
3817
		'themes' => array()
3818
	);
3819
3820
	// Set up some context stuff...
3821
	// Because we're not using numeric indices, we need this to figure out the current table name...
3822
	$keys = array_keys($tables);
3823
3824
	$upcontext['table_count'] = 13;
3825
	$upcontext['cur_table_num'] = $_GET['substep'];
3826
	$upcontext['cur_table_name'] = isset($keys[$_GET['substep']]) ? $keys[$_GET['substep']] : $keys[0];
3827
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3828
	$file_steps = $upcontext['table_count'];
0 ignored issues
show
Unused Code introduced by
$file_steps is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
3829
3830 View Code Duplication
	foreach($keys as $id => $table)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3831
		if ($id < $_GET['substep'])
3832
			$upcontext['previous_tables'][] = $table;
3833
3834
	if ($command_line)
3835
		echo 'Converting data from serialize() to json_encode().';
3836
3837
	if (!$support_js || isset($_GET['xml']))
3838
	{
3839
		// Fix the data in each table
3840
		for ($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
3841
		{
3842
			$upcontext['cur_table_name'] = isset($keys[$substep + 1]) ? $keys[$substep + 1] : $keys[$substep];
3843
			$upcontext['cur_table_num'] = $substep + 1;
3844
3845
			$upcontext['step_progress'] = (int)(($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3846
3847
			// Do we need to pause?
3848
			nextSubstep($substep);
3849
3850
			// Initialize a few things...
3851
			$where = '';
3852
			$vars = array();
3853
			$table = $keys[$substep];
3854
			$info = $tables[$table];
3855
3856
			// Now the fun - build our queries and all that fun stuff
3857
			if ($table == 'settings')
3858
			{
3859
				// Now a few settings...
3860
				$serialized_settings = array(
3861
					'attachment_basedirectories',
3862
					'attachmentUploadDir',
3863
					'cal_today_birthday',
3864
					'cal_today_event',
3865
					'cal_today_holiday',
3866
					'displayFields',
3867
					'last_attachments_directory',
3868
					'memberlist_cache',
3869
					'search_index_custom_config',
3870
					'spider_name_cache'
3871
				);
3872
3873
				// Loop through and fix these...
3874
				$new_settings = array();
3875
				if ($command_line)
3876
					echo "\n" . 'Fixing some settings...';
3877
3878
				foreach ($serialized_settings as $var)
3879
				{
3880
					if (isset($modSettings[$var]))
3881
					{
3882
						// Attempt to unserialize the setting
3883
						$temp = @safe_unserialize($modSettings[$var]);
3884
						if (!$temp && $command_line)
3885
							echo "\n - Failed to unserialize the '" . $var . "' setting. Skipping.";
3886
						elseif ($temp !== false)
3887
							$new_settings[$var] = json_encode($temp);
3888
					}
3889
				}
3890
3891
				// Update everything at once
3892
				if (!function_exists('cache_put_data'))
3893
					require_once($sourcedir . '/Load.php');
3894
				updateSettings($new_settings, true);
3895
3896
				if ($command_line)
3897
					echo ' done.';
3898
			}
3899
			elseif ($table == 'themes')
3900
			{
3901
				// Finally, fix the admin prefs. Unfortunately this is stored per theme, but hopefully they only have one theme installed at this point...
3902
				$query = $smcFunc['db_query']('', '
3903
					SELECT id_member, id_theme, value FROM {db_prefix}themes
3904
					WHERE variable = {string:admin_prefs}',
3905
						array(
3906
							'admin_prefs' => 'admin_preferences'
3907
						)
3908
				);
3909
3910
				if ($smcFunc['db_num_rows']($query) != 0)
3911
				{
3912
					while ($row = $smcFunc['db_fetch_assoc']($query))
3913
					{
3914
						$temp = @safe_unserialize($row['value']);
3915
3916
						if ($command_line)
3917
						{
3918
							if ($temp === false)
3919
								echo "\n" . 'Unserialize of admin_preferences for user ' . $row['id_member'] . ' failed. Skipping.';
3920
							else
3921
								echo "\n" . 'Fixing admin preferences...';
3922
						}
3923
3924
						if ($temp !== false)
3925
						{
3926
							$row['value'] = json_encode($temp);
3927
3928
							// Even though we have all values from the table, UPDATE is still faster than REPLACE
3929
							$smcFunc['db_query']('', '
3930
								UPDATE {db_prefix}themes
3931
								SET value = {string:prefs}
3932
								WHERE id_theme = {int:theme}
3933
									AND id_member = {int:member}',
3934
								array(
3935
									'prefs' => $row['value'],
3936
									'theme' => $row['id_theme'],
3937
									'member' => $row['id_member']
3938
								)
3939
							);
3940
3941
							if ($command_line)
3942
								echo ' done.';
3943
						}
3944
					}
3945
3946
					$smcFunc['db_free_result']($query);
3947
				}
3948
			}
3949
			else
3950
			{
3951
				// First item is always the key...
3952
				$key = $info[0];
3953
				unset($info[0]);
3954
3955
				// Now we know what columns we have and such...
3956
				if (count($info) == 2 && $info[2] === true)
3957
				{
3958
					$col_select = $info[1];
3959
					$where = ' WHERE ' . $info[1] . ' != {empty}';
3960
				}
3961
				else
3962
				{
3963
					$col_select = implode(', ', $info);
3964
				}
3965
3966
				$query = $smcFunc['db_query']('', '
3967
					SELECT ' . $key . ', ' . $col_select . '
3968
					FROM {db_prefix}' . $table . $where,
3969
					array()
3970
				);
3971
3972
				if ($smcFunc['db_num_rows']($query) != 0)
3973
				{
3974
					if ($command_line)
3975
					{
3976
						echo "\n" . ' +++ Fixing the "' . $table . '" table...';
3977
						flush();
3978
					}
3979
3980
					while ($row = $smcFunc['db_fetch_assoc']($query))
3981
					{
3982
						$update = '';
3983
3984
						// We already know what our key is...
3985
						foreach ($info as $col)
3986
						{
3987
							if ($col !== true && $row[$col] != '')
3988
							{
3989
								$temp = @safe_unserialize($row[$col]);
3990
3991
								if ($temp === false && $command_line)
3992
								{
3993
									echo "\nFailed to unserialize " . $row[$col] . "... Skipping\n";
3994
								}
3995
								else
3996
								{
3997
									$row[$col] = json_encode($temp);
3998
3999
									// Build our SET string and variables array
4000
									$update .= (empty($update) ? '' : ', ') . $col . ' = {string:' . $col . '}';
4001
									$vars[$col] = $row[$col];
4002
								}
4003
							}
4004
						}
4005
4006
						$vars[$key] = $row[$key];
4007
4008
						// In a few cases, we might have empty data, so don't try to update in those situations...
4009
						if (!empty($update))
4010
						{
4011
							$smcFunc['db_query']('', '
4012
								UPDATE {db_prefix}' . $table . '
4013
								SET ' . $update . '
4014
								WHERE ' . $key . ' = {' . ($key == 'session' ? 'string' : 'int') . ':' . $key . '}',
4015
								$vars
4016
							);
4017
						}
4018
					}
4019
4020
					if ($command_line)
4021
						echo ' done.';
4022
4023
					// Free up some memory...
4024
					$smcFunc['db_free_result']($query);
4025
				}
4026
			}
4027
			// If this is XML to keep it nice for the user do one table at a time anyway!
4028
			if (isset($_GET['xml']))
4029
				return upgradeExit();
4030
		}
4031
4032
		if ($command_line)
4033
		{
4034
			echo "\n" . 'Successful.' . "\n";
4035
			flush();
4036
		}
4037
		$upcontext['step_progress'] = 100;
4038
4039
		// Last but not least, insert a dummy setting so we don't have to do this again in the future...
4040
		updateSettings(array('json_done' => true));
4041
4042
		$_GET['substep'] = 0;
4043
		// Make sure we move on!
4044
		if ($command_line)
4045
			return DeleteUpgrade();
4046
4047
		return true;
4048
	}
4049
4050
	// If this fails we just move on to deleting the upgrade anyway...
4051
	$_GET['substep'] = 0;
4052
	return false;
4053
}
4054
4055
/******************************************************************************
4056
******************* Templates are below this point ****************************
4057
******************************************************************************/
4058
4059
// This is what is displayed if there's any chmod to be done. If not it returns nothing...
4060
function template_chmod()
4061
{
4062
	global $upcontext, $txt, $settings;
4063
4064
	// Don't call me twice!
4065
	if (!empty($upcontext['chmod_called']))
4066
		return;
4067
4068
	$upcontext['chmod_called'] = true;
4069
4070
	// Nothing?
4071
	if (empty($upcontext['chmod']['files']) && empty($upcontext['chmod']['ftp_error']))
4072
		return;
4073
4074
	// Was it a problem with Windows?
4075
	if (!empty($upcontext['chmod']['ftp_error']) && $upcontext['chmod']['ftp_error'] == 'total_mess')
4076
	{
4077
		echo '
4078
			<div class="error_message red">
4079
				The following files need to be writable to continue the upgrade. Please ensure the Windows permissions are correctly set to allow this:<br>
4080
				<ul style="margin: 2.5ex; font-family: monospace;">
4081
					<li>' . implode('</li>
4082
					<li>', $upcontext['chmod']['files']). '</li>
4083
				</ul>
4084
			</div>';
4085
4086
		return false;
4087
	}
4088
4089
	echo '
4090
		<div class="panel">
4091
			<h2>Your FTP connection information</h2>
4092
			<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>
4093
			<script>
4094
				function warning_popup()
4095
				{
4096
					popup = window.open(\'\',\'popup\',\'height=150,width=400,scrollbars=yes\');
4097
					var content = popup.document;
4098
					content.write(\'<!DOCTYPE html>\n\');
4099
					content.write(\'<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>\n\t<head>\n\t\t<meta name="robots" content="noindex">\n\t\t\');
4100
					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\');
4101
					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\');
4102
					content.write(\'<p>', implode('<br>\n\t\t\t', $upcontext['chmod']['files']), '</p>\n\t\t\t\');';
4103
4104
	if (isset($upcontext['systemos']) && $upcontext['systemos'] == 'linux')
4105
		echo '
4106
					content.write(\'<hr>\n\t\t\t\');
4107
					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\');
4108
					content.write(\'<tt># chmod a+w ', implode(' ', $upcontext['chmod']['files']), '</tt>\n\t\t\t\');';
4109
4110
	echo '
4111
					content.write(\'<a href="javascript:self.close();">close</a>\n\t\t</div>\n\t</body>\n</html>\');
4112
					content.close();
4113
				}
4114
		</script>';
4115
4116
	if (!empty($upcontext['chmod']['ftp_error']))
4117
		echo '
4118
			<div class="error_message red">
4119
				The following error was encountered when trying to connect:<br><br>
4120
				<code>', $upcontext['chmod']['ftp_error'], '</code>
4121
			</div>
4122
			<br>';
4123
4124
	if (empty($upcontext['chmod_in_form']))
4125
		echo '
4126
	<form action="', $upcontext['form_url'], '" method="post">';
4127
4128
	echo '
4129
		<table width="520" border="0" align="center" style="margin-bottom: 1ex;">
4130
			<tr>
4131
				<td width="26%" valign="top" class="textbox"><label for="ftp_server">', $txt['ftp_server'], ':</label></td>
4132
				<td>
4133
					<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>
4134
					<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">
4135
					<div class="smalltext block">', $txt['ftp_server_info'], '</div>
4136
				</td>
4137
			</tr><tr>
4138
				<td width="26%" valign="top" class="textbox"><label for="ftp_username">', $txt['ftp_username'], ':</label></td>
4139
				<td>
4140
					<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">
4141
					<div class="smalltext block">', $txt['ftp_username_info'], '</div>
4142
				</td>
4143
			</tr><tr>
4144
				<td width="26%" valign="top" class="textbox"><label for="ftp_password">', $txt['ftp_password'], ':</label></td>
4145
				<td>
4146
					<input type="password" size="50" name="ftp_password" id="ftp_password" style="width: 99%;" class="input_password">
4147
					<div class="smalltext block">', $txt['ftp_password_info'], '</div>
4148
				</td>
4149
			</tr><tr>
4150
				<td width="26%" valign="top" class="textbox"><label for="ftp_path">', $txt['ftp_path'], ':</label></td>
4151
				<td style="padding-bottom: 1ex;">
4152
					<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">
4153
					<div class="smalltext block">', !empty($upcontext['chmod']['path']) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'], '</div>
4154
				</td>
4155
			</tr>
4156
		</table>
4157
4158
		<div class="righttext" style="margin: 1ex;"><input type="submit" value="', $txt['ftp_connect'], '" class="button_submit"></div>
4159
	</div>';
4160
4161
	if (empty($upcontext['chmod_in_form']))
4162
		echo '
4163
	</form>';
4164
}
4165
4166
function template_upgrade_above()
4167
{
4168
	global $modSettings, $txt, $settings, $upcontext, $upgradeurl;
4169
4170
	echo '<!DOCTYPE html>
4171
<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>
4172
	<head>
4173
		<meta charset="', isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8', '">
4174
		<meta name="robots" content="noindex">
4175
		<title>', $txt['upgrade_upgrade_utility'], '</title>
4176
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/index.css?alp21">
4177
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/install.css?alp21">
4178
		', $txt['lang_rtl'] == true ? '<link rel="stylesheet" href="' . $settings['default_theme_url'] . '/css/rtl.css?alp21">' : '' , '
4179
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
4180
		<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
4181
		<script>
4182
			var smf_scripturl = \'', $upgradeurl, '\';
4183
			var smf_charset = \'', (empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'UTF-8' : $txt['lang_character_set']) : $modSettings['global_character_set']), '\';
4184
			var startPercent = ', $upcontext['overall_percent'], ';
4185
4186
			// This function dynamically updates the step progress bar - and overall one as required.
4187
			function updateStepProgress(current, max, overall_weight)
4188
			{
4189
				// What out the actual percent.
4190
				var width = parseInt((current / max) * 100);
4191
				if (document.getElementById(\'step_progress\'))
4192
				{
4193
					document.getElementById(\'step_progress\').style.width = width + "%";
4194
					setInnerHTML(document.getElementById(\'step_text\'), width + "%");
4195
				}
4196
				if (overall_weight && document.getElementById(\'overall_progress\'))
4197
				{
4198
					overall_width = parseInt(startPercent + width * (overall_weight / 100));
4199
					document.getElementById(\'overall_progress\').style.width = overall_width + "%";
4200
					setInnerHTML(document.getElementById(\'overall_text\'), overall_width + "%");
4201
				}
4202
			}
4203
		</script>
4204
	</head>
4205
	<body>
4206
	<div id="footerfix">
4207
		<div id="header">
4208
			<h1 class="forumtitle">', $txt['upgrade_upgrade_utility'], '</h1>
4209
			<img id="smflogo" src="', $settings['default_theme_url'], '/images/smflogo.png" alt="Simple Machines Forum" title="Simple Machines Forum">
4210
		</div>
4211
	<div id="wrapper">
4212
		<div id="upper_section">
4213
			<div id="inner_section">
4214
				<div id="inner_wrap">
4215
				</div>
4216
			</div>
4217
		</div>
4218
		<div id="content_section">
4219
		<div id="main_content_section">
4220
			<div id="main_steps">
4221
				<h2>', $txt['upgrade_progress'], '</h2>
4222
				<ul>';
4223
4224 View Code Duplication
	foreach ($upcontext['steps'] as $num => $step)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4225
		echo '
4226
						<li class="', $num < $upcontext['current_step'] ? 'stepdone' : ($num == $upcontext['current_step'] ? 'stepcurrent' : 'stepwaiting'), '">', $txt['upgrade_step'], ' ', $step[0], ': ', $step[1], '</li>';
4227
4228
	echo '
4229
					</ul>
4230
			</div>
4231
4232
			<div id="progress_bar">
4233
				<div id="overall_text">', $upcontext['overall_percent'], '%</div>
4234
				<div id="overall_progress" style="width: ', $upcontext['overall_percent'], '%;">
4235
					<span>', $txt['upgrade_overall_progress'], '</span>
4236
				</div>
4237
			</div>';
4238
4239
	if (isset($upcontext['step_progress']))
4240
		echo '
4241
				<br>
4242
				<br>
4243
				<div id="progress_bar_step">
4244
					<div id="step_text">', $upcontext['step_progress'], '%</div>
4245
					<div id="step_progress" style="width: ', $upcontext['step_progress'], '%;background-color: #ffd000;">
4246
						<span>', $txt['upgrade_step_progress'], '</span>
4247
					</div>
4248
				</div>';
4249
4250
	echo '
4251
				<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>
4252
				<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', ';">
4253
					<div id="substep_text" style="color: #000; position: absolute; margin-left: -5em;">', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : '', '%</div>
4254
					<div id="substep_progress" style="width: ', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%; height: 12pt; z-index: 1; background-color: #eebaf4;">&nbsp;</div>
4255
				</div>';
4256
4257
	// How long have we been running this?
4258
	$elapsed = time() - $upcontext['started'];
4259
	$mins = (int) ($elapsed / 60);
4260
	$seconds = $elapsed - $mins * 60;
4261
	echo '
4262
								<br> <br> <br> <br> <br>
4263
								<div class="smalltext" style="padding: 5px; text-align: center;"><br>', $txt['upgrade_time_elapsed'], ':
4264
									<span id="mins_elapsed">', $mins, '</span> ', $txt['upgrade_time_mins'], ', <span id="secs_elapsed">', $seconds, '</span> ', $txt['upgrade_time_secs'], '.
4265
								</div>';
4266
	echo '
4267
			</div>
4268
			</div>
4269
			<div id="main_screen" class="clear">
4270
				<h2>', $upcontext['page_title'], '</h2>
4271
				<div class="panel">
4272
					<div style="max-height: 360px; overflow: auto;">';
4273
}
4274
4275
function template_upgrade_below()
4276
{
4277
	global $upcontext, $txt;
4278
4279
	if (!empty($upcontext['pause']))
4280
		echo '
4281
								<em>', $txt['upgrade_incomplete'], '.</em><br>
4282
4283
								<h2 style="margin-top: 2ex;">', $txt['upgrade_not_quite_done'], '</h2>
4284
								<h3>
4285
									', $txt['upgrade_paused_overload'], '
4286
								</h3>';
4287
4288
	if (!empty($upcontext['custom_warning']))
4289
		echo '
4290
								<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
4291
									<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4292
									<strong style="text-decoration: underline;">', $txt['upgrade_note'], '</strong><br>
4293
									<div style="padding-left: 6ex;">', $upcontext['custom_warning'], '</div>
4294
								</div>';
4295
4296
	echo '
4297
								<div class="righttext" style="margin: 1ex;">';
4298
4299
	if (!empty($upcontext['continue']))
4300
		echo '
4301
									<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '"', $upcontext['continue'] == 2 ? ' disabled' : '', ' class="button_submit">';
4302
	if (!empty($upcontext['skip']))
4303
		echo '
4304
									<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="dontSubmit = true; document.getElementById(\'contbutt\').disabled = \'disabled\'; return true;" class="button_submit">';
4305
4306
	echo '
4307
								</div>
4308
							</form>
4309
						</div>
4310
				</div>
4311
			</div>
4312
			</div>
4313
		</div>
4314
		<div id="footer">
4315
			<ul>
4316
				<li class="copyright"><a href="http://www.simplemachines.org/" title="Simple Machines Forum" target="_blank" class="new_win">SMF &copy; 2016, Simple Machines</a></li>
4317
			</ul>
4318
		</div>
4319
	</body>
4320
</html>';
4321
4322
	// Are we on a pause?
4323
	if (!empty($upcontext['pause']))
4324
	{
4325
		echo '
4326
		<script>
4327
			window.onload = doAutoSubmit;
4328
			var countdown = 3;
4329
			var dontSubmit = false;
4330
4331
			function doAutoSubmit()
4332
			{
4333
				if (countdown == 0 && !dontSubmit)
4334
					document.upform.submit();
4335
				else if (countdown == -1)
4336
					return;
4337
4338
				document.getElementById(\'contbutt\').value = "', $txt['upgrade_continue'], ' (" + countdown + ")";
4339
				countdown--;
4340
4341
				setTimeout("doAutoSubmit();", 1000);
4342
			}
4343
		</script>';
4344
	}
4345
}
4346
4347
function template_xml_above()
4348
{
4349
	global $upcontext;
4350
4351
	echo '<', '?xml version="1.0" encoding="UTF-8"?', '>
4352
	<smf>';
4353
4354
	if (!empty($upcontext['get_data']))
4355
		foreach ($upcontext['get_data'] as $k => $v)
4356
			echo '
4357
		<get key="', $k, '">', $v, '</get>';
4358
}
4359
4360
function template_xml_below()
4361
{
4362
	echo '
4363
		</smf>';
4364
}
4365
4366
function template_error_message()
4367
{
4368
	global $upcontext;
4369
4370
	echo '
4371
	<div class="error_message red">
4372
		', $upcontext['error_msg'], '
4373
		<br>
4374
		<a href="', $_SERVER['PHP_SELF'], '">Click here to try again.</a>
4375
	</div>';
4376
}
4377
4378
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 (L1908-1957) 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...
4379
{
4380
	global $upcontext, $disable_security, $settings, $txt;
4381
4382
	echo '
4383
		<script src="http://www.simplemachines.org/smf/current-version.js?version=' . SMF_VERSION . '"></script>
4384
			<h3>', sprintf($txt['upgrade_ready_proceed'], SMF_VERSION), '</h3>
4385
	<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">
4386
		<input type="hidden" name="', $upcontext['login_token_var'], '" value="', $upcontext['login_token'], '">
4387
		<div id="version_warning" style="margin: 2ex; padding: 2ex; border: 2px dashed #a92174; color: black; background-color: #fbbbe2; display: none;">
4388
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4389
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
4390
			<div style="padding-left: 6ex;">
4391
				', sprintf($txt['upgrade_warning_out_of_date'], SMF_VERSION), '
4392
			</div>
4393
		</div>';
4394
4395
	$upcontext['chmod_in_form'] = true;
4396
	template_chmod();
4397
4398
	// For large, pre 1.1 RC2 forums give them a warning about the possible impact of this upgrade!
4399
	if ($upcontext['is_large_forum'])
4400
		echo '
4401
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
4402
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4403
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
4404
			<div style="padding-left: 6ex;">
4405
				', $txt['upgrade_warning_lots_data'], '
4406
			</div>
4407
		</div>';
4408
4409
	// A warning message?
4410
	if (!empty($upcontext['warning']))
4411
		echo '
4412
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
4413
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4414
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
4415
			<div style="padding-left: 6ex;">
4416
				', $upcontext['warning'], '
4417
			</div>
4418
		</div>';
4419
4420
	// Paths are incorrect?
4421
	echo '
4422
		<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">
4423
			<div style="float: left; width: 2ex; font-size: 2em; color: black;">!!</div>
4424
			<strong style="text-decoration: underline;">', $txt['upgrade_critical_error'], '</strong><br>
4425
			<div style="padding-left: 6ex;">
4426
				', $txt['upgrade_error_script_js'], '
4427
			</div>
4428
		</div>';
4429
4430
	// Is there someone already doing this?
4431
	if (!empty($upcontext['user']['id']) && (time() - $upcontext['started'] < 72600 || time() - $upcontext['updated'] < 3600))
4432
	{
4433
		$ago = time() - $upcontext['started'];
4434
		if ($ago < 60)
4435
			$ago = $ago . ' seconds';
4436
		elseif ($ago < 3600)
4437
			$ago = (int) ($ago / 60) . ' minutes';
4438
		else
4439
			$ago = (int) ($ago / 3600) . ' hours';
4440
4441
		$active = time() - $upcontext['updated'];
4442
		if ($active < 60)
4443
			$updated = $active . ' seconds';
4444
		elseif ($active < 3600)
4445
			$updated = (int) ($active / 60) . ' minutes';
4446
		else
4447
			$updated = (int) ($active / 3600) . ' hours';
4448
4449
		echo '
4450
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
4451
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4452
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
4453
			<div style="padding-left: 6ex;">
4454
				&quot;', $upcontext['user']['name'], '&quot; has been running the upgrade script for the last ', $ago, ' - and was last active ', $updated, ' ago.';
4455
4456
		if ($active < 600)
4457
			echo '
4458
				We recommend that you do not run this script unless you are sure that ', $upcontext['user']['name'], ' has completed their upgrade.';
4459
4460
		if ($active > $upcontext['inactive_timeout'])
4461
			echo '
4462
				<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.';
4463
		else
4464
			echo '
4465
				<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!');
4466
4467
		echo '
4468
			</div>
4469
		</div>';
4470
	}
4471
4472
	echo '
4473
			<strong>Admin Login: ', $disable_security ? '(DISABLED)' : '', '</strong>
4474
			<h3>For security purposes please login with your admin account to proceed with the upgrade.</h3>
4475
			<table>
4476
				<tr valign="top">
4477
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Username:</strong></td>
4478
					<td>
4479
						<input type="text" name="user" value="', !empty($upcontext['username']) ? $upcontext['username'] : '', '"', $disable_security ? ' disabled' : '', ' class="input_text">';
4480
4481
	if (!empty($upcontext['username_incorrect']))
4482
		echo '
4483
						<div class="smalltext" style="color: red;">Username Incorrect</div>';
4484
4485
	echo '
4486
					</td>
4487
				</tr>
4488
				<tr valign="top">
4489
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Password:</strong></td>
4490
					<td>
4491
						<input type="password" name="passwrd" value=""', $disable_security ? ' disabled' : '', ' class="input_password">
4492
						<input type="hidden" name="hash_passwrd" value="">';
4493
4494
	if (!empty($upcontext['password_failed']))
4495
		echo '
4496
						<div class="smalltext" style="color: red;">Password Incorrect</div>';
4497
4498
	echo '
4499
					</td>
4500
				</tr>';
4501
4502
	// Can they continue?
4503
	if (!empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] >= $upcontext['inactive_timeout'] && $upcontext['user']['step'] > 1)
4504
	{
4505
		echo '
4506
				<tr>
4507
					<td colspan="2">
4508
						<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>
4509
					</td>
4510
				</tr>';
4511
	}
4512
4513
	echo '
4514
			</table><br>
4515
			<span class="smalltext">
4516
				<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.
4517
			</span>
4518
			<input type="hidden" name="login_attempt" id="login_attempt" value="1">
4519
			<input type="hidden" name="js_works" id="js_works" value="0">';
4520
4521
	// Say we want the continue button!
4522
	$upcontext['continue'] = !empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] < $upcontext['inactive_timeout'] ? 2 : 1;
4523
4524
	// This defines whether javascript is going to work elsewhere :D
4525
	echo '
4526
		<script>
4527
			if (\'XMLHttpRequest\' in window && document.getElementById(\'js_works\'))
4528
				document.getElementById(\'js_works\').value = 1;
4529
4530
			// Latest version?
4531
			function smfCurrentVersion()
4532
			{
4533
				var smfVer, yourVer;
4534
4535
				if (!(\'smfVersion\' in window))
4536
					return;
4537
4538
				window.smfVersion = window.smfVersion.replace(/SMF\s?/g, \'\');
4539
4540
				smfVer = document.getElementById(\'smfVersion\');
4541
				yourVer = document.getElementById(\'yourVersion\');
4542
4543
				setInnerHTML(smfVer, window.smfVersion);
4544
4545
				var currentVersion = getInnerHTML(yourVer);
4546
				if (currentVersion < window.smfVersion)
4547
					document.getElementById(\'version_warning\').style.display = \'\';
4548
			}
4549
			addLoadEvent(smfCurrentVersion);
4550
4551
			// This checks that the script file even exists!
4552
			if (typeof(smfSelectText) == \'undefined\')
4553
				document.getElementById(\'js_script_missing_error\').style.display = \'\';
4554
4555
		</script>';
4556
}
4557
4558
function template_upgrade_options()
4559
{
4560
	global $upcontext, $modSettings, $db_prefix, $mmessage, $mtitle, $db_type;
4561
4562
	echo '
4563
			<h3>Before the upgrade gets underway please review the options below - and hit continue when you\'re ready to begin.</h3>
4564
			<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">';
4565
4566
	// Warning message?
4567
	if (!empty($upcontext['upgrade_options_warning']))
4568
		echo '
4569
		<div style="margin: 1ex; padding: 1ex; border: 1px dashed #cc3344; color: black; background-color: #ffe4e9;">
4570
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4571
			<strong style="text-decoration: underline;">Warning!</strong><br>
4572
			<div style="padding-left: 4ex;">
4573
				', $upcontext['upgrade_options_warning'], '
4574
			</div>
4575
		</div>';
4576
4577
	echo '
4578
				<table>
4579
					<tr valign="top">
4580
						<td width="2%">
4581
							<input type="checkbox" name="backup" id="backup" value="1"', $db_type != 'mysql' && $db_type != 'mysqli' && $db_type != 'postgresql' ? ' disabled' : '', ' class="input_check">
4582
						</td>
4583
						<td width="100%">
4584
							<label for="backup">Backup tables in your database with the prefix &quot;backup_' . $db_prefix . '&quot;.</label>', isset($modSettings['smfVersion']) ? '' : ' (recommended!)', '
4585
						</td>
4586
					</tr>
4587
					<tr valign="top">
4588
						<td width="2%">
4589
							<input type="checkbox" name="maint" id="maint" value="1" checked class="input_check">
4590
						</td>
4591
						<td width="100%">
4592
							<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>
4593
							<div id="mainmess" style="display: none;">
4594
								<strong class="smalltext">Maintenance Title: </strong><br>
4595
								<input type="text" name="maintitle" size="30" value="', htmlspecialchars($mtitle), '" class="input_text"><br>
4596
								<strong class="smalltext">Maintenance Message: </strong><br>
4597
								<textarea name="mainmessage" rows="3" cols="50">', htmlspecialchars($mmessage), '</textarea>
4598
							</div>
4599
						</td>
4600
					</tr>';
4601
4602
	// Offer mysql users to switch to mysqli
4603
	if ($db_type == 'mysql' && function_exists('mysqli_query'))
4604
		echo '
4605
					<tr valign="top">
4606
						<td width="2%">
4607
							<input type="checkbox" name="convertMysql" id="convertMysql" value="1" checked class="input_check">
4608
						</td>
4609
						<td width="100%">
4610
							<label for="convertMysql">Use MySQLi functionality (MySQL compatible).</span>
4611
							<strong class="smalltext"><a href="http://wiki.simplemachines.org/smf/Upgrading-MySQLi-Functionality" target="_blank">More information about MySQLi</a></strong><br>
4612
						</td>
4613
					</tr>';
4614
4615
	echo '
4616
					<tr valign="top">
4617
						<td width="2%">
4618
							<input type="checkbox" name="debug" id="debug" value="1" class="input_check">
4619
						</td>
4620
						<td width="100%">
4621
							<label for="debug">Output extra debugging information</label>
4622
						</td>
4623
					</tr>
4624
					<tr valign="top">
4625
						<td width="2%">
4626
							<input type="checkbox" name="empty_error" id="empty_error" value="1" class="input_check">
4627
						</td>
4628
						<td width="100%">
4629
							<label for="empty_error">Empty error log before upgrading</label>
4630
						</td>
4631
					</tr>';
4632
4633
	if (!empty($upcontext['karma_installed']['good']) || !empty($upcontext['karma_installed']['bad']))
4634
		echo '
4635
					<tr valign="top">
4636
						<td width="2%">
4637
							<input type="checkbox" name="delete_karma" id="delete_karma" value="1" class="input_check">
4638
						</td>
4639
						<td width="100%">
4640
							<label for="delete_karma">Delete all karma settings and info from the DB</label>
4641
						</td>
4642
					</tr>';
4643
4644
	echo '
4645
					<tr valign="top">
4646
						<td width="2%">
4647
							<input type="checkbox" name="stat" id="stat" value="1"', empty($modSettings['allow_sm_stats']) ? '' : ' checked', ' class="input_check">
4648
						</td>
4649
						<td width="100%">
4650
							<label for="stat">
4651
								Allow Simple Machines to Collect Basic Stats Monthly.<br>
4652
								<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>
4653
							</label>
4654
						</td>
4655
					</tr>
4656
				</table>
4657
				<input type="hidden" name="upcont" value="1">';
4658
4659
	// We need a normal continue button here!
4660
	$upcontext['continue'] = 1;
4661
}
4662
4663
// Template for the database backup tool/
4664
function template_backup_database()
4665
{
4666
	global $upcontext, $support_js, $is_debug;
4667
4668
	echo '
4669
			<h3>Please wait while a backup is created. For large forums this may take some time!</h3>';
4670
4671
	echo '
4672
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4673
			<input type="hidden" name="backup_done" id="backup_done" value="0">
4674
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
4675
			<span id="debuginfo"></span>';
4676
4677
	// Dont any tables so far?
4678
	if (!empty($upcontext['previous_tables']))
4679
		foreach ($upcontext['previous_tables'] as $table)
4680
			echo '
4681
			<br>Completed Table: &quot;', $table, '&quot;.';
4682
4683
	echo '
4684
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
4685
			<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>';
4686
4687
	// Continue please!
4688
	$upcontext['continue'] = $support_js ? 2 : 1;
4689
4690
	// If javascript allows we want to do this using XML.
4691
	if ($support_js)
4692
	{
4693
		echo '
4694
		<script>
4695
			var lastTable = ', $upcontext['cur_table_num'], ';
4696
			function getNextTables()
4697
			{
4698
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4699
			}
4700
4701
			// Got an update!
4702
			function onBackupUpdate(oXMLDoc)
4703
			{
4704
				var sCurrentTableName = "";
4705
				var iTableNum = 0;
4706
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4707
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4708
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4709
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4710
4711
				// Update the page.
4712
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4713
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4714
				lastTable = iTableNum;
4715
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4716
4717
		// If debug flood the screen.
4718
		if ($is_debug)
4719
			echo '
4720
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4721
4722
				if (document.getElementById(\'debuginfo\').scrollHeight)
4723
					document.getElementById(\'debuginfo\').scrollTop = document.getElementById(\'debuginfo\').scrollHeight;';
4724
4725
		echo '
4726
				// Get the next update...
4727
				if (iTableNum == ', $upcontext['table_count'], ')
4728
				{
4729
					document.getElementById(\'commess\').style.display = "";
4730
					document.getElementById(\'current_tab_div\').style.display = "none";
4731
					document.getElementById(\'contbutt\').disabled = 0;
4732
					document.getElementById(\'backup_done\').value = 1;
4733
				}
4734
				else
4735
					getNextTables();
4736
			}
4737
			getNextTables();
4738
		</script>';
4739
	}
4740
}
4741
4742
function template_backup_xml()
4743
{
4744
	global $upcontext;
4745
4746
	echo '
4747
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4748
}
4749
4750
// Here is the actual "make the changes" template!
4751
function template_database_changes()
4752
{
4753
	global $upcontext, $support_js, $is_debug, $timeLimitThreshold;
4754
4755
	if (empty($is_debug) && !empty($upcontext['upgrade_status']['debug']))
4756
		$is_debug = true;
4757
4758
	echo '
4759
		<h3>Executing database changes</h3>
4760
		<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>';
4761
4762
	echo '
4763
		<form action="', $upcontext['form_url'], '&amp;filecount=', $upcontext['file_count'], '" name="upform" id="upform" method="post">
4764
		<input type="hidden" name="database_done" id="database_done" value="0">';
4765
4766
	// No javascript looks rubbish!
4767
	if (!$support_js)
4768
	{
4769
		foreach ($upcontext['actioned_items'] as $num => $item)
4770
		{
4771
			if ($num != 0)
4772
				echo ' Successful!';
4773
			echo '<br>' . $item;
4774
		}
4775
		if (!empty($upcontext['changes_complete']))
4776
		{
4777
			if ($is_debug)
4778
			{
4779
				$active = time() - $upcontext['started'];
4780
				$hours = floor($active / 3600);
4781
				$minutes = intval(($active / 60) % 60);
4782
				$seconds = intval($active % 60);
4783
4784
				$totalTime = '';
4785
				if ($hours > 0)
4786
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's':'') . ' ';
4787
				if ($minutes > 0)
4788
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's':'') . ' ';
4789
				if ($seconds > 0)
4790
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's':'') . ' ';
4791
			}
4792
4793
			if ($is_debug && !empty($totalTime))
4794
				echo ' Successful! Completed in ', $totalTime, '<br><br>';
4795
			else
4796
				echo ' Successful!<br><br>';
4797
4798
			echo '<span id="commess" style="font-weight: bold;">1 Database Updates Complete! Click Continue to Proceed.</span><br>';
4799
		}
4800
	}
4801
	else
4802
	{
4803
		// Tell them how many files we have in total.
4804
		if ($upcontext['file_count'] > 1)
4805
			echo '
4806
		<strong id="info1">Executing upgrade script <span id="file_done">', $upcontext['cur_file_num'], '</span> of ', $upcontext['file_count'], '.</strong>';
4807
4808
		echo '
4809
		<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>
4810
		<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>';
4811
4812
		if ($is_debug)
4813
		{
4814
			if ($upcontext['current_debug_item_num'] == $upcontext['debug_items'])
4815
			{
4816
				$active = time() - $upcontext['started'];
4817
				$hours = floor($active / 3600);
4818
				$minutes = intval(($active / 60) % 60);
4819
				$seconds = intval($active % 60);
4820
4821
				$totalTime = '';
4822
				if ($hours > 0)
4823
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's':'') . ' ';
4824
				if ($minutes > 0)
4825
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's':'') . ' ';
4826
				if ($seconds > 0)
4827
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's':'') . ' ';
4828
			}
4829
4830
			echo '
4831
			<br><span id="upgradeCompleted">';
4832
4833
			if (!empty($totalTime))
4834
				echo 'Completed in ', $totalTime, '<br>';
4835
4836
			echo '</span>
4837
			<div id="debug_section" style="height: 200px; overflow: auto;">
4838
			<span id="debuginfo"></span>
4839
			</div>';
4840
		}
4841
	}
4842
4843
	// Place for the XML error message.
4844
	echo '
4845
		<div id="error_block" style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9; display: ', empty($upcontext['error_message']) ? 'none' : '', ';">
4846
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4847
			<strong style="text-decoration: underline;">Error!</strong><br>
4848
			<div style="padding-left: 6ex;" id="error_message">', isset($upcontext['error_message']) ? $upcontext['error_message'] : 'Unknown Error!', '</div>
4849
		</div>';
4850
4851
	// We want to continue at some point!
4852
	$upcontext['continue'] = $support_js ? 2 : 1;
4853
4854
	// If javascript allows we want to do this using XML.
4855
	if ($support_js)
4856
	{
4857
		echo '
4858
		<script>
4859
			var lastItem = ', $upcontext['current_debug_item_num'], ';
4860
			var sLastString = "', strtr($upcontext['current_debug_item_name'], array('"' => '&quot;')), '";
4861
			var iLastSubStepProgress = -1;
4862
			var curFile = ', $upcontext['cur_file_num'], ';
4863
			var totalItems = 0;
4864
			var prevFile = 0;
4865
			var retryCount = 0;
4866
			var testvar = 0;
4867
			var timeOutID = 0;
4868
			var getData = "";
4869
			var debugItems = ', $upcontext['debug_items'], ';';
4870
4871
		if ($is_debug)
4872
			echo '
4873
			var upgradeStartTime = ' . $upcontext['started'] . ';';
4874
4875
		echo '
4876
			function getNextItem()
4877
			{
4878
				// We want to track this...
4879
				if (timeOutID)
4880
					clearTimeout(timeOutID);
4881
				timeOutID = window.setTimeout("retTimeout()", ', (10 * $timeLimitThreshold), '000);
4882
4883
				getXMLDocument(\'', $upcontext['form_url'], '&xml&filecount=', $upcontext['file_count'], '&substep=\' + lastItem + getData, onItemUpdate);
4884
			}
4885
4886
			// Got an update!
4887
			function onItemUpdate(oXMLDoc)
4888
			{
4889
				var sItemName = "";
4890
				var sDebugName = "";
4891
				var iItemNum = 0;
4892
				var iSubStepProgress = -1;
4893
				var iDebugNum = 0;
4894
				var bIsComplete = 0;
4895
				getData = "";
4896
4897
				// We\'ve got something - so reset the timeout!
4898
				if (timeOutID)
4899
					clearTimeout(timeOutID);
4900
4901
				// Assume no error at this time...
4902
				document.getElementById("error_block").style.display = "none";
4903
4904
				// Are we getting some duff info?
4905
				if (!oXMLDoc.getElementsByTagName("item")[0])
4906
				{
4907
					// Too many errors?
4908
					if (retryCount > 15)
4909
					{
4910
						document.getElementById("error_block").style.display = "";
4911
						setInnerHTML(document.getElementById("error_message"), "Error retrieving information on step: " + (sDebugName == "" ? sLastString : sDebugName));';
4912
4913
	if ($is_debug)
4914
		echo '
4915
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4916
4917
	echo '
4918
					}
4919
					else
4920
					{
4921
						retryCount++;
4922
						getNextItem();
4923
					}
4924
					return false;
4925
				}
4926
4927
				// Never allow loops.
4928
				if (curFile == prevFile)
4929
				{
4930
					retryCount++;
4931
					if (retryCount > 10)
4932
					{
4933
						document.getElementById("error_block").style.display = "";
4934
						setInnerHTML(document.getElementById("error_message"), "Upgrade script appears to be going into a loop - step: " + sDebugName);';
4935
4936
	if ($is_debug)
4937
		echo '
4938
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4939
4940
	echo '
4941
					}
4942
				}
4943
				retryCount = 0;
4944
4945
				for (var i = 0; i < oXMLDoc.getElementsByTagName("item")[0].childNodes.length; i++)
4946
					sItemName += oXMLDoc.getElementsByTagName("item")[0].childNodes[i].nodeValue;
4947
				for (var i = 0; i < oXMLDoc.getElementsByTagName("debug")[0].childNodes.length; i++)
4948
					sDebugName += oXMLDoc.getElementsByTagName("debug")[0].childNodes[i].nodeValue;
4949
				for (var i = 0; i < oXMLDoc.getElementsByTagName("get").length; i++)
4950
				{
4951
					getData += "&" + oXMLDoc.getElementsByTagName("get")[i].getAttribute("key") + "=";
4952
					for (var j = 0; j < oXMLDoc.getElementsByTagName("get")[i].childNodes.length; j++)
4953
					{
4954
						getData += oXMLDoc.getElementsByTagName("get")[i].childNodes[j].nodeValue;
4955
					}
4956
				}
4957
4958
				iItemNum = oXMLDoc.getElementsByTagName("item")[0].getAttribute("num");
4959
				iDebugNum = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("num"));
4960
				bIsComplete = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("complete"));
4961
				iSubStepProgress = parseFloat(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("percent"));
4962
				sLastString = sDebugName + " (Item: " + iDebugNum + ")";
4963
4964
				curFile = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("num"));
4965
				debugItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("debug_items"));
4966
				totalItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("items"));
4967
4968
				// If we have an error we haven\'t completed!
4969
				if (oXMLDoc.getElementsByTagName("error")[0] && bIsComplete)
4970
					iDebugNum = lastItem;
4971
4972
				// Do we have the additional progress bar?
4973
				if (iSubStepProgress != -1)
4974
				{
4975
					document.getElementById("substep_bar_div").style.display = "";
4976
					document.getElementById("substep_bar_div2").style.display = "";
4977
					document.getElementById("substep_progress").style.width = iSubStepProgress + "%";
4978
					setInnerHTML(document.getElementById("substep_text"), iSubStepProgress + "%");
4979
					setInnerHTML(document.getElementById("substep_bar_div"), sDebugName.replace(/\./g, "") + ":");
4980
				}
4981
				else
4982
				{
4983
					document.getElementById("substep_bar_div").style.display = "none";
4984
					document.getElementById("substep_bar_div2").style.display = "none";
4985
				}
4986
4987
				// Move onto the next item?
4988
				if (bIsComplete)
4989
					lastItem = iDebugNum;
4990
				else
4991
					lastItem = iDebugNum - 1;
4992
4993
				// Are we finished?
4994
				if (bIsComplete && iDebugNum == -1 && curFile >= ', $upcontext['file_count'], ')
4995
				{';
4996
4997
		if ($is_debug)
4998
			echo '
4999
					document.getElementById(\'debug_section\').style.display = "none";
5000
5001
					var upgradeFinishedTime = parseInt(oXMLDoc.getElementsByTagName("curtime")[0].childNodes[0].nodeValue);
5002
					var diffTime = upgradeFinishedTime - upgradeStartTime;
5003
					var diffHours = Math.floor(diffTime / 3600);
5004
					var diffMinutes = parseInt((diffTime / 60) % 60);
5005
					var diffSeconds = parseInt(diffTime % 60);
5006
5007
					var totalTime = "";
5008
					if (diffHours > 0)
5009
						totalTime = totalTime + diffHours + " hour" + (diffHours > 1 ? "s" : "") + " ";
5010
					if (diffMinutes > 0)
5011
						totalTime = totalTime + diffMinutes + " minute" + (diffMinutes > 1 ? "s" : "") + " ";
5012
					if (diffSeconds > 0)
5013
						totalTime = totalTime + diffSeconds + " second" + (diffSeconds > 1 ? "s" : "");
5014
5015
					setInnerHTML(document.getElementById("upgradeCompleted"), "Completed in " + totalTime);';
5016
5017
		echo '
5018
5019
					document.getElementById(\'commess\').style.display = "";
5020
					document.getElementById(\'contbutt\').disabled = 0;
5021
					document.getElementById(\'database_done\').value = 1;';
5022
5023
		if ($upcontext['file_count'] > 1)
5024
			echo '
5025
					document.getElementById(\'info1\').style.display = "none";';
5026
5027
		echo '
5028
					document.getElementById(\'info2\').style.display = "none";
5029
					updateStepProgress(100, 100, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
5030
					return true;
5031
				}
5032
				// Was it the last step in the file?
5033
				else if (bIsComplete && iDebugNum == -1)
5034
				{
5035
					lastItem = 0;
5036
					prevFile = curFile;';
5037
5038
		if ($is_debug)
5039
			echo '
5040
					setOuterHTML(document.getElementById(\'debuginfo\'), \'Moving to next script file...done<br><span id="debuginfo"><\' + \'/span>\');';
5041
5042
		echo '
5043
					getNextItem();
5044
					return true;
5045
				}';
5046
5047
		// If debug scroll the screen.
5048
		if ($is_debug)
5049
			echo '
5050
				if (iLastSubStepProgress == -1)
5051
				{
5052
					// Give it consistent dots.
5053
					dots = sDebugName.match(/\./g);
5054
					numDots = dots ? dots.length : 0;
5055
					for (var i = numDots; i < 3; i++)
5056
						sDebugName += ".";
5057
					setOuterHTML(document.getElementById(\'debuginfo\'), sDebugName + \'<span id="debuginfo"><\' + \'/span>\');
5058
				}
5059
				iLastSubStepProgress = iSubStepProgress;
5060
5061
				if (bIsComplete)
5062
					setOuterHTML(document.getElementById(\'debuginfo\'), \'done<br><span id="debuginfo"><\' + \'/span>\');
5063
				else
5064
					setOuterHTML(document.getElementById(\'debuginfo\'), \'...<span id="debuginfo"><\' + \'/span>\');
5065
5066
				if (document.getElementById(\'debug_section\').scrollHeight)
5067
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
5068
5069
		echo '
5070
				// Update the page.
5071
				setInnerHTML(document.getElementById(\'item_num\'), iItemNum);
5072
				setInnerHTML(document.getElementById(\'cur_item_name\'), sItemName);';
5073
5074
		if ($upcontext['file_count'] > 1)
5075
		{
5076
			echo '
5077
				setInnerHTML(document.getElementById(\'file_done\'), curFile);
5078
				setInnerHTML(document.getElementById(\'item_count\'), totalItems);';
5079
		}
5080
5081
		echo '
5082
				// Is there an error?
5083
				if (oXMLDoc.getElementsByTagName("error")[0])
5084
				{
5085
					var sErrorMsg = "";
5086
					for (var i = 0; i < oXMLDoc.getElementsByTagName("error")[0].childNodes.length; i++)
5087
						sErrorMsg += oXMLDoc.getElementsByTagName("error")[0].childNodes[i].nodeValue;
5088
					document.getElementById("error_block").style.display = "";
5089
					setInnerHTML(document.getElementById("error_message"), sErrorMsg);
5090
					return false;
5091
				}
5092
5093
				// Get the progress bar right.
5094
				barTotal = debugItems * ', $upcontext['file_count'], ';
5095
				barDone = (debugItems * (curFile - 1)) + lastItem;
5096
5097
				updateStepProgress(barDone, barTotal, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
5098
5099
				// Finally - update the time here as it shows the server is responding!
5100
				curTime = new Date();
5101
				iElapsed = (curTime.getTime() / 1000 - ', $upcontext['started'], ');
5102
				mins = parseInt(iElapsed / 60);
5103
				secs = parseInt(iElapsed - mins * 60);
5104
				setInnerHTML(document.getElementById("mins_elapsed"), mins);
5105
				setInnerHTML(document.getElementById("secs_elapsed"), secs);
5106
5107
				getNextItem();
5108
				return true;
5109
			}
5110
5111
			// What if we timeout?!
5112
			function retTimeout(attemptAgain)
5113
			{
5114
				// Oh noes...
5115
				if (!attemptAgain)
5116
				{
5117
					document.getElementById("error_block").style.display = "";
5118
					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");
5119
				}
5120
				else
5121
				{
5122
					document.getElementById("error_block").style.display = "none";
5123
					getNextItem();
5124
				}
5125
			}';
5126
5127
		// Start things off assuming we've not errored.
5128
		if (empty($upcontext['error_message']))
5129
			echo '
5130
			getNextItem();';
5131
5132
		echo '
5133
		</script>';
5134
	}
5135
	return;
5136
}
5137
5138
function template_database_xml()
5139
{
5140
	global $is_debug, $upcontext, $txt;
5141
5142
	echo '
5143
	<file num="', $upcontext['cur_file_num'], '" items="', $upcontext['total_items'], '" debug_items="', $upcontext['debug_items'], '">', $upcontext['cur_file_name'], '</file>
5144
	<item num="', $upcontext['current_item_num'], '">', $upcontext['current_item_name'], '</item>
5145
	<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>';
5146
5147
	if (!empty($upcontext['error_message']))
5148
		echo '
5149
	<error>', $upcontext['error_message'], '</error>';
5150
5151
	if (!empty($upcontext['error_string']))
5152
		echo '
5153
	<sql>', $upcontext['error_string'], '</sql>';
5154
5155
	if ($is_debug)
5156
		echo '
5157
	<curtime>', time(), '</curtime>';
5158
}
5159
5160
// Template for the UTF-8 conversion step. Basically a copy of the backup stuff with slight modifications....
5161 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...
5162
{
5163
	global $upcontext, $support_js, $is_debug;
5164
5165
	echo '
5166
			<h3>Please wait while your database is converted to UTF-8. For large forums this may take some time!</h3>';
5167
5168
	echo '
5169
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
5170
			<input type="hidden" name="utf8_done" id="utf8_done" value="0">
5171
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
5172
			<span id="debuginfo"></span>';
5173
5174
	// Done any tables so far?
5175
	if (!empty($upcontext['previous_tables']))
5176
		foreach ($upcontext['previous_tables'] as $table)
5177
			echo '
5178
			<br>Completed Table: &quot;', $table, '&quot;.';
5179
5180
	echo '
5181
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>';
5182
5183
	// If we dropped their index, let's let them know
5184
	if ($upcontext['cur_table_num'] == $upcontext['table_count'] && $upcontext['dropping_index'])
5185
		echo '
5186
			<br><span style="display:inline;">Please note that your fulltext index was dropped to facilitate the conversion and will need to be recreated.</span>';
5187
5188
	echo '
5189
			<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>';
5190
5191
	// Continue please!
5192
	$upcontext['continue'] = $support_js ? 2 : 1;
5193
5194
	// If javascript allows we want to do this using XML.
5195
	if ($support_js)
5196
	{
5197
		echo '
5198
		<script>
5199
			var lastTable = ', $upcontext['cur_table_num'], ';
5200
			function getNextTables()
5201
			{
5202
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
5203
			}
5204
5205
			// Got an update!
5206
			function onBackupUpdate(oXMLDoc)
5207
			{
5208
				var sCurrentTableName = "";
5209
				var iTableNum = 0;
5210
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
5211
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
5212
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
5213
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
5214
5215
				// Update the page.
5216
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
5217
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
5218
				lastTable = iTableNum;
5219
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
5220
5221
		// If debug flood the screen.
5222
		if ($is_debug)
5223
			echo '
5224
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');';
5225
5226
		echo '
5227
				// Get the next update...
5228
				if (iTableNum == ', $upcontext['table_count'], ')
5229
				{
5230
					document.getElementById(\'commess\').style.display = "";
5231
					document.getElementById(\'current_tab_div\').style.display = "none";
5232
					document.getElementById(\'contbutt\').disabled = 0;
5233
					document.getElementById(\'utf8_done\').value = 1;
5234
				}
5235
				else
5236
					getNextTables();
5237
			}
5238
			getNextTables();
5239
		</script>';
5240
	}
5241
}
5242
5243
function template_utf8_xml()
5244
{
5245
	global $upcontext;
5246
5247
	echo '
5248
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
5249
}
5250
5251
function template_clean_mods()
5252
{
5253
	global $upcontext;
5254
5255
	$upcontext['chmod_in_form'] = true;
5256
5257
	echo '
5258
	<h3>SMF has detected some packages which were installed but not fully removed prior to upgrade. We recommend you remove the following mods and reinstall upon completion of the upgrade.</h3>
5259
	<form action="', $upcontext['form_url'], '&amp;ssi=1" name="upform" id="upform" method="post">';
5260
5261
	// In case it's required.
5262
	template_chmod();
5263
5264
	echo '
5265
		<table width="90%" align="center" style="background-color: black;">
5266
			<tr style="background-color: #eeeeee;">
5267
				<td width="40%"><strong>Modification Name</strong></td>
5268
				<td width="10%" align="center"><strong>Version</strong></td>
5269
				<td width="15%"><strong>Files Affected</strong></td>
5270
				<td width="20%"><strong>Status</strong></td>
5271
				<td width="5%" align="center"><strong>Fix?</strong></td>
5272
			</tr>';
5273
5274
	foreach ($upcontext['packages'] as $package)
5275
	{
5276
		echo '
5277
			<tr style="background-color: #cccccc;">
5278
				<td width="40%">', $package['name'], '</td>
5279
				<td width="10%">', $package['version'], '</td>
5280
				<td width="15%">', $package['file_count'], ' <span class="smalltext">[<a href="#" onclick="alert(\'The following files are affected by this modification:\\n\\n', strtr(implode('<br>', $package['files']), array('\\' => '\\\\', '<br>' => '\\n')), '\'); return false;">details</a>]</td>
5281
				<td width="20%"><span style="font-weight: bold; color: ', $package['color'], '">', $package['status'], '</span></td>
5282
				<td width="5%" align="center">
5283
					<input type="hidden" name="remove[', $package['id'], ']" value="0">
5284
					<input type="checkbox" name="remove[', $package['id'], ']"', $package['color'] == 'green' ? ' disabled' : '', ' class="input_check">
5285
				</td>
5286
			</tr>';
5287
	}
5288
	echo '
5289
		</table>
5290
		<input type="hidden" name="cleandone" value="1">';
5291
5292
	// Files to make writable?
5293 View Code Duplication
	if (!empty($upcontext['writable_files']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
5294
		echo '
5295
		<input type="hidden" name="writable_files" value="', base64_encode(safe_serialize($upcontext['writable_files'])), '">';
5296
5297
	// We'll want a continue button...
5298
	if (empty($upcontext['chmod']['files']))
5299
		$upcontext['continue'] = 1;
5300
}
5301
5302
// Finished with the mods - let them know what we've done.
5303
function template_cleanup_done()
5304
{
5305
	global $upcontext;
5306
5307
	echo '
5308
	<h3>SMF has attempted to fix and reinstall mods as required. We recommend you visit the package manager upon completing upgrade to check the status of your modifications.</h3>
5309
	<form action="', $upcontext['form_url'], '&amp;ssi=1" name="upform" id="upform" method="post">
5310
		<table width="90%" align="center" style="background-color: black;">
5311
			<tr style="background-color: #eeeeee;">
5312
				<td width="100%"><strong>Actions Completed:</strong></td>
5313
			</tr>';
5314
5315
	foreach ($upcontext['packages'] as $package)
5316
	{
5317
		echo '
5318
			<tr style="background-color: #cccccc;">
5319
				<td>', $package['name'], '... <span style="font-weight: bold; color: ', $package['color'], ';">', $package['result'], '</span></td>
5320
			</tr>';
5321
	}
5322
	echo '
5323
		</table>
5324
		<input type="hidden" name="cleandone2" value="1">';
5325
5326
	// We'll want a continue button...
5327
	$upcontext['continue'] = 1;
5328
}
5329
5330
// Do they want to upgrade their templates?
5331
function template_upgrade_templates()
5332
{
5333
	global $upcontext;
5334
5335
	echo '
5336
	<h3>There have been numerous language and template changes since the previous version of SMF. On this step the upgrader can attempt to automatically make these changes in your templates to save you from doing so manually.</h3>
5337
	<form action="', $upcontext['form_url'], '&amp;ssi=1', $upcontext['is_test'] ? '' : ';forreal=1', '" name="upform" id="upform" method="post">';
5338
5339
	// Any files need to be writable?
5340
	$upcontext['chmod_in_form'] = true;
5341
	template_chmod();
5342
5343
	// Language/Template files need an update?
5344
	if ($upcontext['temp_progress'] == 0 && !$upcontext['is_test'] && (!empty($upcontext['languages']) || !empty($upcontext['themes'])))
5345
	{
5346
		echo '
5347
		The following template files will be updated to ensure they are compatible with this version of SMF. Note that this can only fix a limited number of compatibility issues and in general you should seek out the latest version of these themes/language files.
5348
		<table width="90%" align="center" style="background-color: black;">
5349
			<tr style="background-color: #eeeeee;">
5350
				<td width="80%"><strong>Area</strong></td>
5351
				<td width="20%" align="center"><strong>Changes Required</strong></td>
5352
			</tr>';
5353
5354 View Code Duplication
		foreach ($upcontext['languages'] as $language)
0 ignored issues
show
Bug introduced by
The expression $upcontext['languages'] of type boolean is not traversable.
Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
5355
		{
5356
			echo '
5357
				<tr style="background-color: #cccccc;">
5358
					<td width="80%">
5359
						&quot;', $language['name'], '&quot; Language Pack
5360
						<div class="smalltext">(';
5361
5362
			foreach ($language['files'] as $k => $file)
5363
				echo $file['name'], $k + 1 != count($language['files']) ? ', ' : ')';
5364
5365
			echo '
5366
						</div>
5367
					</td>
5368
					<td width="20%" align="center">', $language['edit_count'] == 0 ? 1 : $language['edit_count'], '</td>
5369
				</tr>';
5370
		}
5371
5372 View Code Duplication
		foreach ($upcontext['themes'] as $theme)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
5373
		{
5374
			echo '
5375
				<tr style="background-color: #CCCCCC;">
5376
					<td width="80%">
5377
						&quot;', $theme['name'], '&quot; Theme
5378
						<div class="smalltext">(';
5379
5380
			foreach ($theme['files'] as $k => $file)
5381
				echo $file['name'], $k + 1 != count($theme['files']) ? ', ' : ')';
5382
5383
			echo '
5384
						</div>
5385
					</td>
5386
					<td width="20%" align="center">', $theme['edit_count'] == 0 ? 1 : $theme['edit_count'], '</td>
5387
				</tr>';
5388
		}
5389
5390
		echo '
5391
		</table>';
5392
	}
5393
	else
5394
	{
5395
		$langFiles = 0;
5396
		$themeFiles = 0;
5397
		if (!empty($upcontext['languages']))
5398
			foreach ($upcontext['languages'] as $lang)
0 ignored issues
show
Bug introduced by
The expression $upcontext['languages'] of type boolean is not traversable.
Loading history...
5399
				$langFiles += count($lang['files']);
5400
		if (!empty($upcontext['themes']))
5401
			foreach ($upcontext['themes'] as $theme)
0 ignored issues
show
Bug introduced by
The expression $upcontext['themes'] of type boolean is not traversable.
Loading history...
5402
				$themeFiles += count($theme['files']);
5403
		echo sprintf('Found <strong>%d</strong> language files and <strong>%d</strong> templates requiring an update so far.', $langFiles, $themeFiles) . '<br>';
5404
5405
		// What we're currently doing?
5406
		if (!empty($upcontext['current_message']))
5407
			echo '
5408
				', $upcontext['current_message'];
5409
	}
5410
5411
	echo '
5412
		<input type="hidden" name="uptempdone" value="1">';
5413
5414 View Code Duplication
	if (!empty($upcontext['languages']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
5415
		echo '
5416
		<input type="hidden" name="languages" value="', base64_encode(safe_serialize($upcontext['languages'])), '">';
5417 View Code Duplication
	if (!empty($upcontext['themes']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
5418
		echo '
5419
		<input type="hidden" name="themes" value="', base64_encode(safe_serialize($upcontext['themes'])), '">';
5420 View Code Duplication
	if (!empty($upcontext['writable_files']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
5421
		echo '
5422
		<input type="hidden" name="writable_files" value="', base64_encode(safe_serialize($upcontext['writable_files'])), '">';
5423
5424
	// Offer them the option to upgrade from YaBB SE?
5425
	if (!empty($upcontext['can_upgrade_yabbse']))
5426
		echo '
5427
		<br><label for="conv"><input type="checkbox" name="conv" id="conv" value="1" class="input_check"> Convert the existing YaBB SE template and set it as default.</label><br>';
5428
5429
	// We'll want a continue button... assuming chmod is OK (Otherwise let them use connect!)
5430
	if (empty($upcontext['chmod']['files']) || $upcontext['is_test'])
5431
		$upcontext['continue'] = 1;
5432
}
5433
5434
// Template for the database backup tool/
5435 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...
5436
{
5437
	global $upcontext, $support_js, $is_debug;
5438
5439
	echo '
5440
			<h3>Converting data from serialize to JSON...</h3>';
5441
5442
	echo '
5443
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
5444
			<input type="hidden" name="json_done" id="json_done" value="0">
5445
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
5446
			<span id="debuginfo"></span>';
5447
5448
	// Dont any tables so far?
5449
	if (!empty($upcontext['previous_tables']))
5450
		foreach ($upcontext['previous_tables'] as $table)
5451
			echo '
5452
			<br>Completed Table: &quot;', $table, '&quot;.';
5453
5454
	echo '
5455
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
5456
			<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>';
5457
5458
	// Try to make sure substep was reset.
5459
	if ($upcontext['cur_table_num'] == $upcontext['table_count'])
5460
		echo '
5461
			<input type="hidden" name="substep" id="substep" value="0">';
5462
5463
	// Continue please!
5464
	$upcontext['continue'] = $support_js ? 2 : 1;
5465
5466
	// If javascript allows we want to do this using XML.
5467
	if ($support_js)
5468
	{
5469
		echo '
5470
		<script>
5471
			var lastTable = ', $upcontext['cur_table_num'], ';
5472
			function getNextTables()
5473
			{
5474
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
5475
			}
5476
5477
			// Got an update!
5478
			function onBackupUpdate(oXMLDoc)
5479
			{
5480
				var sCurrentTableName = "";
5481
				var iTableNum = 0;
5482
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
5483
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
5484
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
5485
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
5486
5487
				// Update the page.
5488
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
5489
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
5490
				lastTable = iTableNum;
5491
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
5492
5493
		// If debug flood the screen.
5494
		if ($is_debug)
5495
			echo '
5496
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');';
5497
5498
		echo '
5499
				// Get the next update...
5500
				if (iTableNum == ', $upcontext['table_count'], ')
5501
				{
5502
					document.getElementById(\'commess\').style.display = "";
5503
					document.getElementById(\'current_tab_div\').style.display = "none";
5504
					document.getElementById(\'contbutt\').disabled = 0;
5505
					document.getElementById(\'json_done\').value = 1;
5506
				}
5507
				else
5508
					getNextTables();
5509
			}
5510
			getNextTables();
5511
		</script>';
5512
	}
5513
}
5514
5515
function template_serialize_json_xml()
5516
{
5517
	global $upcontext;
5518
5519
	echo '
5520
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
5521
}
5522
5523
function template_upgrade_complete()
5524
{
5525
	global $upcontext, $upgradeurl, $settings, $boardurl, $is_debug;
5526
5527
	echo '
5528
	<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>
5529
	<form action="', $boardurl, '/index.php">';
5530
5531
	if (!empty($upcontext['can_delete_script']))
5532
		echo '
5533
			<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>
5534
			<script>
5535
				function doTheDelete(theCheck)
5536
				{
5537
					var theImage = document.getElementById ? document.getElementById("delete_upgrader") : document.all.delete_upgrader;
5538
5539
					theImage.src = "', $upgradeurl, '?delete=1&ts_" + (new Date().getTime());
5540
					theCheck.disabled = true;
5541
				}
5542
			</script>
5543
			<img src="', $settings['default_theme_url'], '/images/blank.png" alt="" id="delete_upgrader"><br>';
5544
5545
	$active = time() - $upcontext['started'];
5546
	$hours = floor($active / 3600);
5547
	$minutes = intval(($active / 60) % 60);
5548
	$seconds = intval($active % 60);
5549
5550
	if ($is_debug)
5551
	{
5552
		$totalTime = '';
5553
		if ($hours > 0)
5554
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's':'') . ' ';
5555
		if ($minutes > 0)
5556
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's':'') . ' ';
5557
		if ($seconds > 0)
5558
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's':'') . ' ';
5559
	}
5560
5561
	if ($is_debug && !empty($totalTime))
5562
		echo '<br> Upgrade completed in ', $totalTime, '<br><br>';
5563
5564
	echo '<br>
5565
			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>
5566
			<br>
5567
			Best of luck,<br>
5568
			Simple Machines';
5569
}
5570
5571
/**
5572
 * Convert MySQL (var)char ip col to binary
5573
 * newCol needs to be a varbinary(16) null able field
5574
 * return true or false
5575
 */
5576
function MySQLConvertOldIp($targetTable, $oldCol, $newCol, $limit = 50000, $setSize = 100)
5577
{
5578
	global $smcFunc, $step_progress;
5579
5580
	$step_progress['name'] = 'Converting ips';
5581
	$step_progress['current'] = $_GET['a'];
5582
5583
	// Skip this if we don't have the column
5584
	$request = $smcFunc['db_query']('', '
5585
		SHOW FIELDS
5586
		FROM {db_prefix}{raw:table}
5587
		WHERE Field = {string:name}',
5588
		array(
5589
			'table' => $targetTable,
5590
			'name' => $oldCol,
5591
	));
5592
	if ($smcFunc['db_num_rows']($request) !== 1)
5593
	{
5594
		$smcFunc['db_free_result']($request);
5595
		return;
5596
	}
5597
	$smcFunc['db_free_result']($request);
5598
5599
	//mysql default max length is 1mb http://dev.mysql.com/doc/refman/5.1/en/packet-too-large.html
5600
	$arIp = array();
5601
5602
	$is_done = false;
5603
	while (!$is_done)
5604
	{
5605
		nextSubStep($substep);
0 ignored issues
show
Bug introduced by
The variable $substep does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
5606
5607
		$request = $smcFunc['db_query']('', '
5608
			SELECT DISTINCT {raw:old_col}
5609
			FROM {db_prefix}{raw:table_name}
5610
			WHERE {raw:new_col} IS NULL
5611
			LIMIT {int:limit}',
5612
			array(
5613
				'old_col' => $oldCol,
5614
				'new_col' => $newCol,
5615
				'table_name' => $targetTable,
5616
				'empty' => '',
5617
				'limit' => $limit,
5618
		));
5619
		while ($row = $smcFunc['db_fetch_assoc']($request))
5620
			$arIp[] = $row[$oldCol];
5621
		$smcFunc['db_free_result']($request);
5622
5623
		// Special case, null ip could keep us in a loop.
5624
		if (is_null($arIp[0]))
5625
			unset($arIp[0]);
5626
5627
		if (empty($arIp))
5628
			$is_done = true;
5629
5630
		$updates = array();
5631
		$cases = array();
5632
		$count = count($arIp);
5633
		for ($i = 0; $i < $count; $i++)
5634
		{
5635
			$arIp[$i] = trim($arIp[$i]);
5636
5637
			if (empty($arIp[$i]))
5638
				continue;
5639
5640
			$updates['ip' . $i] = $arIp[$i];
5641
			$cases[$arIp[$i]] = 'WHEN ' . $oldCol . ' = {string:ip' . $i . '} THEN {inet:ip' . $i . '}';
5642
5643
			if ($setSize > 0 && $i % $setSize === 0)
5644
			{
5645
				if (count($updates) == 1)
5646
					continue;
5647
5648
				$updates['whereSet'] = array_values($updates);
5649
				$smcFunc['db_query']('', '
5650
					UPDATE {db_prefix}' . $targetTable . '
5651
					SET ' . $newCol . ' = CASE ' .
5652
					implode('
5653
						', $cases) . '
5654
						ELSE NULL
5655
					END
5656
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
5657
					$updates
5658
				);
5659
5660
				$updates = array();
5661
				$cases = array();
5662
			}
5663
		}
5664
5665
		// Incase some extras made it through.
5666
		if (!empty($updates))
5667
		{
5668
			if (count($updates) == 1)
5669
			{
5670
				foreach ($updates as $key => $ip)
5671
				{
5672
					$request = $smcFunc['db_query']('', '
0 ignored issues
show
Unused Code introduced by
$request is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
5673
						UPDATE {db_prefix}' . $targetTable . '
5674
						SET ' . $newCol . ' = {inet:ip}
5675
						WHERE ' . $oldCol . ' = {string:ip}',
5676
						array(
5677
							'ip' => $ip
5678
					));
5679
				}
5680
			}
5681
			else
5682
			{
5683
				$updates['whereSet'] = array_values($updates);
5684
				$request = $smcFunc['db_query']('', '
0 ignored issues
show
Unused Code introduced by
$request is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
5685
					UPDATE {db_prefix}' . $targetTable . '
5686
					SET ' . $newCol . ' = CASE ' .
5687
					implode('
5688
						', $cases) . '
5689
						ELSE NULL
5690
					END
5691
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
5692
					$updates
5693
				);
5694
			}
5695
		}
5696
		else
5697
			$is_done = true;
5698
5699
		$_GET['a'] += $limit;
5700
		$step_progress['current'] = $_GET['a'];
5701
	}
5702
5703
	unset($_GET['a']);
5704
}
5705
5706
?>
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...
5707