Completed
Push — release-2.1 ( 5f908d...084464 )
by Mert
12:22
created

upgrade.php ➔ template_upgrade_above()   C

Complexity

Conditions 14
Paths 4

Size

Total Lines 105

Duplication

Lines 3
Ratio 2.86 %

Importance

Changes 0
Metric Value
cc 14
nc 4
nop 0
dl 3
loc 105
rs 5.0133
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines http://www.simplemachines.org
8
 * @copyright 2018 Simple Machines and individual contributors
9
 * @license http://www.simplemachines.org/about/smf/license.php BSD
10
 *
11
 * @version 2.1 Beta 4
12
 */
13
14
// Version information...
15
define('SMF_VERSION', '2.1 Beta 4');
16
define('SMF_LANG_VERSION', '2.1 Beta 4');
17
18
/**
19
 * The minimum required PHP version.
20
 * @var string
21
 */
22
$GLOBALS['required_php_version'] = '5.4.0';
23
24
/**
25
 * A list of supported database systems.
26
 * @var array
27
 */
28
$databases = array(
29
	'mysql' => array(
30
		'name' => 'MySQL',
31
		'version' => '5.0.22',
32
		'version_check' => 'global $db_connection; return min(mysqli_get_server_info($db_connection), mysqli_get_client_info());',
33
		'utf8_support' => true,
34
		'utf8_version' => '5.0.22',
35
		'utf8_version_check' => 'global $db_connection; return mysqli_get_server_info($db_connection);',
36
		'alter_support' => true,
37
	),
38
	'postgresql' => array(
39
		'name' => 'PostgreSQL',
40
		'version' => '9.4',
41
		'version_check' => '$version = pg_version(); return $version[\'client\'];',
42
		'always_has_db' => true,
43
	),
44
);
45
46
/**
47
 * The maximum time a single substep may take, in seconds.
48
 * @var int
49
 */
50
$timeLimitThreshold = 3;
51
52
/**
53
 * The current path to the upgrade.php file.
54
 * @var string
55
 */
56
$upgrade_path = dirname(__FILE__);
57
58
/**
59
 * The URL of the current page.
60
 * @var string
61
 */
62
$upgradeurl = $_SERVER['PHP_SELF'];
63
64
/**
65
 * Flag to disable the required administrator login.
66
 * @var bool
67
 */
68
$disable_security = false;
69
70
/**
71
 * The amount of seconds allowed between logins.
72
 * If the first user to login is inactive for this amount of seconds, a second login is allowed.
73
 * @var int
74
 */
75
$upcontext['inactive_timeout'] = 10;
76
77
// The helper is crucial. Include it first thing.
78
if (!file_exists($upgrade_path . '/upgrade-helper.php'))
79
	die('upgrade-helper.php not found where it was expected: ' . $upgrade_path . '/upgrade-helper.php! Make sure you have uploaded ALL files from the upgrade package. The upgrader cannot continue.');
80
81
require_once($upgrade_path . '/upgrade-helper.php');
82
83
global $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
84
85
// Initialize everything and load the language files.
86
initialize_inputs();
87
load_lang_file();
88
89
90
// All the steps in detail.
91
// Number,Name,Function,Progress Weight.
92
$upcontext['steps'] = array(
93
	0 => array(1, $txt['upgrade_step_login'], 'WelcomeLogin', 2),
94
	1 => array(2, $txt['upgrade_step_options'], 'UpgradeOptions', 2),
95
	2 => array(3, $txt['upgrade_step_backup'], 'BackupDatabase', 10),
96
	3 => array(4, $txt['upgrade_step_database'], 'DatabaseChanges', 50),
97
	4 => array(5, $txt['upgrade_step_convertutf'], 'ConvertUtf8', 20),
98
	5 => array(6, $txt['upgrade_step_convertjson'], 'serialize_to_json', 10),
99
	6 => array(7, $txt['upgrade_step_delete'], 'DeleteUpgrade', 1),
100
);
101
// Just to remember which one has files in it.
102
$upcontext['database_step'] = 3;
103
@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...
104
if (!ini_get('safe_mode'))
105
{
106
	ini_set('mysql.connect_timeout', -1);
107
	ini_set('default_socket_timeout', 900);
108
}
109
// Clean the upgrade path if this is from the client.
110
if (!empty($_SERVER['argv']) && php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
111
	for ($i = 1; $i < $_SERVER['argc']; $i++)
112
	{
113
		if (preg_match('~^--path=(.+)$~', $_SERVER['argv'][$i], $match) != 0)
114
			$upgrade_path = substr($match[1], -1) == '/' ? substr($match[1], 0, -1) : $match[1];
115
	}
116
117
// Are we from the client?
118
if (php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
119
{
120
	$command_line = true;
121
	$disable_security = true;
122
}
123
else
124
	$command_line = false;
125
126
// Load this now just because we can.
127
require_once($upgrade_path . '/Settings.php');
128
129
// We don't use "-utf8" anymore...  Tweak the entry that may have been loaded by Settings.php
130
if (isset($language))
131
	$language = str_ireplace('-utf8', '', $language);
132
133
// Are we logged in?
134
if (isset($upgradeData))
135
{
136
	$upcontext['user'] = json_decode(base64_decode($upgradeData), true);
137
138
	// Check for sensible values.
139 View Code Duplication
	if (empty($upcontext['user']['started']) || $upcontext['user']['started'] < time() - 86400)
140
		$upcontext['user']['started'] = time();
141 View Code Duplication
	if (empty($upcontext['user']['updated']) || $upcontext['user']['updated'] < time() - 86400)
142
		$upcontext['user']['updated'] = 0;
143
144
	$upcontext['started'] = $upcontext['user']['started'];
145
	$upcontext['updated'] = $upcontext['user']['updated'];
146
147
	$is_debug = !empty($upcontext['user']['debug']) ? true : false;
148
}
149
150
// Nothing sensible?
151
if (empty($upcontext['updated']))
152
{
153
	$upcontext['started'] = time();
154
	$upcontext['updated'] = 0;
155
	$upcontext['user'] = array(
156
		'id' => 0,
157
		'name' => 'Guest',
158
		'pass' => 0,
159
		'started' => $upcontext['started'],
160
		'updated' => $upcontext['updated'],
161
	);
162
}
163
164
// Load up some essential data...
165
loadEssentialData();
166
167
// Are we going to be mimic'ing SSI at this point?
168
if (isset($_GET['ssi']))
169
{
170
	require_once($sourcedir . '/Errors.php');
171
	require_once($sourcedir . '/Logging.php');
172
	require_once($sourcedir . '/Load.php');
173
	require_once($sourcedir . '/Security.php');
174
	require_once($sourcedir . '/Subs-Package.php');
175
176
	// SMF isn't started up properly, but loadUserSettings calls our cookies.
177 View Code Duplication
	if (!isset($smcFunc['json_encode']))
178
	{
179
		$smcFunc['json_encode'] = 'json_encode';
180
		$smcFunc['json_decode'] = 'smf_json_decode';
181
	}
182
183
	loadUserSettings();
184
	loadPermissions();
185
}
186
187
// Include our helper functions.
188
require_once($sourcedir . '/Subs.php');
189
require_once($sourcedir . '/LogInOut.php');
190
191
// This only exists if we're on SMF ;)
192
if (isset($modSettings['smfVersion']))
193
{
194
	$request = $smcFunc['db_query']('', '
195
		SELECT variable, value
196
		FROM {db_prefix}themes
197
		WHERE id_theme = {int:id_theme}
198
			AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
199
		array(
200
			'id_theme' => 1,
201
			'theme_url' => 'theme_url',
202
			'theme_dir' => 'theme_dir',
203
			'images_url' => 'images_url',
204
			'db_error_skip' => true,
205
		)
206
	);
207 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
208
		$modSettings[$row['variable']] = $row['value'];
209
	$smcFunc['db_free_result']($request);
210
}
211
212
if (!isset($modSettings['theme_url']))
213
{
214
	$modSettings['theme_dir'] = $boarddir . '/Themes/default';
215
	$modSettings['theme_url'] = 'Themes/default';
216
	$modSettings['images_url'] = 'Themes/default/images';
217
}
218
if (!isset($settings['default_theme_url']))
219
	$settings['default_theme_url'] = $modSettings['theme_url'];
220
if (!isset($settings['default_theme_dir']))
221
	$settings['default_theme_dir'] = $modSettings['theme_dir'];
222
223
// This is needed in case someone invokes the upgrader using https when upgrading an http forum
224 View Code Duplication
if (httpsOn())
225
	$settings['default_theme_url'] = strtr($settings['default_theme_url'], array('http://' => 'https://'));
226
227
$upcontext['is_large_forum'] = (empty($modSettings['smfVersion']) || $modSettings['smfVersion'] <= '1.1 RC1') && !empty($modSettings['totalMessages']) && $modSettings['totalMessages'] > 75000;
228
// Default title...
229
$upcontext['page_title'] = $txt['updating_smf_installation'];
230
231
// Have we got tracking data - if so use it (It will be clean!)
232
if (isset($_GET['data']))
233
{
234
	global $is_debug;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
235
236
	$upcontext['upgrade_status'] = json_decode(base64_decode($_GET['data']), true);
237
	$upcontext['current_step'] = $upcontext['upgrade_status']['curstep'];
238
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
239
	$upcontext['rid'] = $upcontext['upgrade_status']['rid'];
240
	$support_js = $upcontext['upgrade_status']['js'];
241
242
	// Only set this if the upgrader status says so.
243
	if (empty($is_debug))
244
		$is_debug = $upcontext['upgrade_status']['debug'];
245
246
	// Load the language.
247 View Code Duplication
	if (file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
248
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
249
}
250
// Set the defaults.
251
else
252
{
253
	$upcontext['current_step'] = 0;
254
	$upcontext['rid'] = mt_rand(0, 5000);
255
	$upcontext['upgrade_status'] = array(
256
		'curstep' => 0,
257
		'lang' => isset($_GET['lang']) ? $_GET['lang'] : basename($language, '.lng'),
258
		'rid' => $upcontext['rid'],
259
		'pass' => 0,
260
		'debug' => 0,
261
		'js' => 0,
262
	);
263
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
264
}
265
266
// If this isn't the first stage see whether they are logging in and resuming.
267
if ($upcontext['current_step'] != 0 || !empty($upcontext['user']['step']))
268
	checkLogin();
269
270
if ($command_line)
271
	cmdStep0();
272
273
// Don't error if we're using xml.
274
if (isset($_GET['xml']))
275
	$upcontext['return_error'] = true;
276
277
// Loop through all the steps doing each one as required.
278
$upcontext['overall_percent'] = 0;
279
foreach ($upcontext['steps'] as $num => $step)
280
{
281
	if ($num >= $upcontext['current_step'])
282
	{
283
		// The current weight of this step in terms of overall progress.
284
		$upcontext['step_weight'] = $step[3];
285
		// Make sure we reset the skip button.
286
		$upcontext['skip'] = false;
287
288
		// We cannot proceed if we're not logged in.
289
		if ($num != 0 && !$disable_security && $upcontext['user']['pass'] != $upcontext['upgrade_status']['pass'])
290
		{
291
			$upcontext['steps'][0][2]();
292
			break;
293
		}
294
295
		// Call the step and if it returns false that means pause!
296
		if (function_exists($step[2]) && $step[2]() === false)
297
			break;
298
		elseif (function_exists($step[2])) {
299
			//Start each new step with this unset, so the 'normal' template is called first
300
			unset($_GET['xml']);
301
			//Clear out warnings at the start of each step
302
			unset($upcontext['custom_warning']);
303
			$_GET['substep'] = 0;
304
			$upcontext['current_step']++;
305
		}
306
	}
307
	$upcontext['overall_percent'] += $step[3];
308
}
309
310
upgradeExit();
311
312
// Exit the upgrade script.
313
function upgradeExit($fallThrough = false)
314
{
315
	global $upcontext, $upgradeurl, $sourcedir, $command_line, $is_debug, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
316
317
	// Save where we are...
318
	if (!empty($upcontext['current_step']) && !empty($upcontext['user']['id']))
319
	{
320
		$upcontext['user']['step'] = $upcontext['current_step'];
321
		$upcontext['user']['substep'] = $_GET['substep'];
322
		$upcontext['user']['updated'] = time();
323
		$upcontext['debug'] = $is_debug;
324
		$upgradeData = base64_encode(json_encode($upcontext['user']));
325
		require_once($sourcedir . '/Subs-Admin.php');
326
		updateSettingsFile(array('upgradeData' => '"' . $upgradeData . '"'));
327
		updateDbLastError(0);
0 ignored issues
show
Unused Code introduced by
The call to updateDbLastError() has too many arguments starting with 0.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
328
	}
329
330
	// Handle the progress of the step, if any.
331
	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
332
	{
333
		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
334
		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
335
	}
336
	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
337
338
	// We usually dump our templates out.
339
	if (!$fallThrough)
340
	{
341
		// This should not happen my dear... HELP ME DEVELOPERS!!
342
		if (!empty($command_line))
343
		{
344
			if (function_exists('debug_print_backtrace'))
345
				debug_print_backtrace();
346
347
			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.';
348
			flush();
349
			die();
350
		}
351
352
		if (!isset($_GET['xml']))
353
			template_upgrade_above();
354
		else
355
		{
356
			header('content-type: text/xml; charset=UTF-8');
357
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
358
			$upcontext['get_data'] = array();
359
			foreach ($_GET as $k => $v)
360
			{
361
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
362
				{
363
					$upcontext['get_data'][$k] = $v;
364
				}
365
			}
366
			template_xml_above();
367
		}
368
369
		// Call the template.
370
		if (isset($upcontext['sub_template']))
371
		{
372
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
373
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(json_encode($upcontext['upgrade_status']));
374
375
			// Custom stuff to pass back?
376
			if (!empty($upcontext['query_string']))
377
				$upcontext['form_url'] .= $upcontext['query_string'];
378
379
			// Call the appropriate subtemplate
380
			if (is_callable('template_' . $upcontext['sub_template']))
381
				call_user_func('template_' . $upcontext['sub_template']);
382
			else
383
				die('Upgrade aborted!  Invalid template: template_' . $upcontext['sub_template']);
384
		}
385
386
		// Was there an error?
387
		if (!empty($upcontext['forced_error_message']))
388
			echo $upcontext['forced_error_message'];
389
390
		// Show the footer.
391
		if (!isset($_GET['xml']))
392
			template_upgrade_below();
393
		else
394
			template_xml_below();
395
	}
396
397
398
	if (!empty($command_line) && $is_debug)
399
	{
400
		$active = time() - $upcontext['started'];
401
		$hours = floor($active / 3600);
402
		$minutes = intval(($active / 60) % 60);
403
		$seconds = intval($active % 60);
404
405
		$totalTime = '';
406 View Code Duplication
		if ($hours > 0)
407
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
408 View Code Duplication
		if ($minutes > 0)
409
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
410 View Code Duplication
		if ($seconds > 0)
411
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
412
413
		if (!empty($totalTime))
414
			echo "\n" . '', $txt['upgrade_completed_time'], ' ' . $totalTime . "\n";
415
	}
416
417
	// Bang - gone!
418
	die();
419
}
420
421
// Load the list of language files, and the current language file.
422
function load_lang_file()
0 ignored issues
show
Best Practice introduced by
The function load_lang_file() has been defined more than once; this definition is ignored, only the first definition in other/install.php (L267-350) 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...
423
{
424
	global $txt, $incontext, $user_info;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
425
426
	$incontext['detected_languages'] = array();
427
428
	// Make sure the languages directory actually exists.
429 View Code Duplication
	if (file_exists(dirname(__FILE__) . '/Themes/default/languages'))
430
	{
431
		// Find all the "Install" language files in the directory.
432
		$dir = dir(dirname(__FILE__) . '/Themes/default/languages');
433
		while ($entry = $dir->read())
434
		{
435
			if (substr($entry, 0, 8) == 'Install.' && substr($entry, -4) == '.php')
436
				$incontext['detected_languages'][$entry] = ucfirst(substr($entry, 8, strlen($entry) - 12));
437
		}
438
		$dir->close();
439
	}
440
441
	// Didn't find any, show an error message!
442 View Code Duplication
	if (empty($incontext['detected_languages']))
443
	{
444
		// Let's not cache this message, eh?
445
		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
446
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
447
		header('Cache-Control: no-cache');
448
449
		echo '<!DOCTYPE html>
450
			<html>
451
				<head>
452
					<title>SMF Upgrader: Error!</title>
453
						<style>
454
							body {
455
								font-family: sans-serif;
456
								max-width: 700px; }
457
458
								h1 {
459
									font-size: 14pt; }
460
461
								.directory {
462
									margin: 0.3em;
463
									font-family: monospace;
464
									font-weight: bold; }
465
						</style>
466
				</head>
467
				<body>
468
					<h1>A critical error has occurred.</h1>
469
						<p>This upgrader was unable to find the upgrader\'s language file or files.  They should be found under:</p>
470
						<div class="directory">', dirname($_SERVER['PHP_SELF']) != '/' ? dirname($_SERVER['PHP_SELF']) : '', '/Themes/default/languages</div>
471
						<p>In some cases, FTP clients do not properly upload files with this many folders. Please double check to make sure you <strong>have uploaded all the files in the distribution</strong>.</p>
472
						<p>If that doesn\'t help, please make sure this install.php file is in the same place as the Themes folder.</p>
473
						<p>If you continue to get this error message, feel free to <a href="https://support.simplemachines.org/">look to us for support</a>.</p>
474
				</body>
475
			</html>';
476
		die;
477
	}
478
479
	// Override the language file?
480 View Code Duplication
	if (isset($_GET['lang_file']))
481
		$_SESSION['installer_temp_lang'] = $_GET['lang_file'];
482
	elseif (isset($GLOBALS['HTTP_GET_VARS']['lang_file']))
483
		$_SESSION['installer_temp_lang'] = $GLOBALS['HTTP_GET_VARS']['lang_file'];
484
485
	// Make sure it exists, if it doesn't reset it.
486
	if (!isset($_SESSION['installer_temp_lang']) || preg_match('~[^\\w_\\-.]~', $_SESSION['installer_temp_lang']) === 1 || !file_exists(dirname(__FILE__) . '/Themes/default/languages/' . $_SESSION['installer_temp_lang']))
487
	{
488
		// Use the first one...
489
		list ($_SESSION['installer_temp_lang']) = array_keys($incontext['detected_languages']);
490
491
		// If we have english and some other language, use the other language.  We Americans hate english :P.
492 View Code Duplication
		if ($_SESSION['installer_temp_lang'] == 'Install.english.php' && count($incontext['detected_languages']) > 1)
493
			list (, $_SESSION['installer_temp_lang']) = array_keys($incontext['detected_languages']);
494
495
		// For backup we load the english at first -> second language overwrite the english one
496
		if (count($incontext['detected_languages']) > 1)
497
			require_once(dirname(__FILE__) . '/Themes/default/languages/Install.english.php');
498
	}
499
500
	// And now include the actual language file itself.
501
	require_once(dirname(__FILE__) . '/Themes/default/languages/' . $_SESSION['installer_temp_lang']);
502
503
	// Which language did we load? Assume that he likes his language.
504
	preg_match('~^Install\.(.+[^-utf8])\.php$~', $_SESSION['installer_temp_lang'], $matches);
505
	if (empty($matches[1]))
506
		$matches = [
507
			0 => 'nothing',
508
			1 => 'english',
509
		];
510
	$user_info['language'] = $matches[1];
511
}
512
513
// Used to direct the user to another location.
514
function redirectLocation($location, $addForm = true)
515
{
516
	global $upgradeurl, $upcontext, $command_line;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
517
518
	// Command line users can't be redirected.
519
	if ($command_line)
520
		upgradeExit(true);
521
522
	// Are we providing the core info?
523
	if ($addForm)
524
	{
525
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
526
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
527
	}
528
529
	while (@ob_end_clean());
530
	header('location: ' . strtr($location, array('&amp;' => '&')));
531
532
	// Exit - saving status as we go.
533
	upgradeExit(true);
534
}
535
536
// Load all essential data and connect to the DB as this is pre SSI.php
537
function loadEssentialData()
538
{
539
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection, $db_prefix, $db_character_set, $db_type;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
540
	global $modSettings, $sourcedir, $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
541
542
	error_reporting(E_ALL);
543
	define('SMF', 1);
544
545
	// Start the session.
546
	if (@ini_get('session.save_handler') == 'user')
547
		@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...
548
	@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...
549
550
	if (empty($smcFunc))
551
		$smcFunc = array();
552
553
	// We need this for authentication and some upgrade code
554
	require_once($sourcedir . '/Subs-Auth.php');
555
	require_once($sourcedir . '/Class-Package.php');
556
557
	$smcFunc['strtolower'] = 'smf_strtolower';
558
559
	// Initialize everything...
560
	initialize_inputs();
561
562
	// Get the database going!
563
	if (empty($db_type) || $db_type == 'mysqli')
564
	{
565
		$db_type = 'mysql';
566
		// If overriding $db_type, need to set its settings.php entry too
567
		$changes = array();
568
		$changes['db_type'] = '\'mysql\'';
569
		require_once($sourcedir . '/Subs-Admin.php');
570
		updateSettingsFile($changes);
571
	}
572
573
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
574
	{
575
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
576
577
		// Make the connection...
578
		if (empty($db_connection))
579
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, array('non_fatal' => true));
580
		else
581
			// If we've returned here, ping/reconnect to be safe
582
			$smcFunc['db_ping']($db_connection);
583
584
		// Oh dear god!!
585
		if ($db_connection === null)
586
			die('Unable to connect to database - please check username and password are correct in Settings.php');
587
588
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
589
			$smcFunc['db_query']('', '
590
			SET NAMES {string:db_character_set}',
591
			array(
592
				'db_error_skip' => true,
593
				'db_character_set' => $db_character_set,
594
			)
595
		);
596
597
		// Load the modSettings data...
598
		$request = $smcFunc['db_query']('', '
599
			SELECT variable, value
600
			FROM {db_prefix}settings',
601
			array(
602
				'db_error_skip' => true,
603
			)
604
		);
605
		$modSettings = array();
606 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
607
			$modSettings[$row['variable']] = $row['value'];
608
		$smcFunc['db_free_result']($request);
609
	}
610
	else
611
	{
612
		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.');
613
	}
614
615
	require_once($sourcedir . '/Subs.php');
616
617
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
618
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
619
	{
620
		require_once($sourcedir . '/QueryString.php');
621
		cleanRequest();
622
	}
623
624
	if (!isset($_GET['substep']))
625
		$_GET['substep'] = 0;
626
}
627
628
function initialize_inputs()
0 ignored issues
show
Best Practice introduced by
The function initialize_inputs() has been defined more than once; this definition is ignored, only the first definition in other/install.php (L145-264) 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...
629
{
630
	global $start_time, $db_type;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
631
632
	$start_time = time();
633
634
	umask(0);
635
636
	ob_start();
637
638
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
639
	ignore_user_abort(true);
640
641
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
642
	if (isset($_GET['delete']))
643
	{
644
		@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...
645
646
		// And the extra little files ;).
647
		@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...
648
		@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...
649
		@unlink(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
650
		@unlink(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
651
		@unlink(dirname(__FILE__) . '/upgrade-helper.php');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
652
653
		$dh = opendir(dirname(__FILE__));
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $dh. Configured minimum length is 3.

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

Loading history...
654
		while ($file = readdir($dh))
655
		{
656
			if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
657
				@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...
658
		}
659
		closedir($dh);
660
661
		// Legacy files while we're at it. NOTE: We only touch files we KNOW shouldn't be there.
662
		// 1.1 Sources files not in 2.0+
663
		@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...
664
		// 1.1 Templates that don't exist any more (e.g. renamed)
665
		@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...
666
		@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...
667
		// 1.1 JS files were stored in the main theme folder, but in 2.0+ are in the scripts/ folder
668
		@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...
669
		@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...
670
		@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...
671
		@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...
672
		@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...
673
674
		// 2.0 Sources files not in 2.1+
675
		@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...
676
		@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...
677
678
		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');
679
		exit;
680
	}
681
682
	// Something is causing this to happen, and it's annoying.  Stop it.
683
	$temp = 'upgrade_php?step';
684
	while (strlen($temp) > 4)
685
	{
686
		if (isset($_GET[$temp]))
687
			unset($_GET[$temp]);
688
		$temp = substr($temp, 1);
689
	}
690
691
	// Force a step, defaulting to 0.
692
	$_GET['step'] = (int) @$_GET['step'];
693
	$_GET['substep'] = (int) @$_GET['substep'];
694
}
695
696
// Step 0 - Let's welcome them in and ask them to login!
697
function WelcomeLogin()
698
{
699
	global $boarddir, $sourcedir, $modSettings, $cachedir, $upgradeurl, $upcontext;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
700
	global $smcFunc, $db_type, $databases, $boardurl;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
701
702
	// We global $txt here so that the language files can add to them. This variable is NOT unused.
703
	global $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
704
705
	$upcontext['sub_template'] = 'welcome_message';
706
707
	// Check for some key files - one template, one language, and a new and an old source file.
708
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
709
		&& @file_exists($sourcedir . '/QueryString.php')
710
		&& @file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')
711
		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
712
713
	// Need legacy scripts?
714 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.1)
715
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
716 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.0)
717
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-1.sql');
718 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 1.1)
719
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
720
721
	// We don't need "-utf8" files anymore...
722
	$upcontext['language'] = str_ireplace('-utf8', '', $upcontext['language']);
723
724
	// This needs to exist!
725
	if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
726
		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>]');
727
	else
728
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
729
730
	if (!$check)
731
		// 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.
732
		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.');
733
734
	// Do they meet the install requirements?
735
	if (!php_version_check())
736
		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.');
737
738
	if (!db_version_check())
739
		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.');
740
741
	// Do some checks to make sure they have proper privileges
742
	db_extend('packages');
743
744
	// CREATE
745
	$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');
746
747
	// ALTER
748
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
749
750
	// DROP
751
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
752
753
	// Sorry... we need CREATE, ALTER and DROP
754 View Code Duplication
	if (!$create || !$alter || !$drop)
755
		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.');
756
757
	// Do a quick version spot check.
758
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
759
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
760
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
761
		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.');
762
763
	// What absolutely needs to be writable?
764
	$writable_files = array(
765
		$boarddir . '/Settings.php',
766
		$boarddir . '/Settings_bak.php',
767
	);
768
769
	// Only check for minified writable files if we have it enabled or not set.
770
	if (!empty($modSettings['minimize_files']) || !isset($modSettings['minimize_files']))
771
		$writable_files += array(
772
			$modSettings['theme_dir'] . '/css/minified.css',
773
			$modSettings['theme_dir'] . '/scripts/minified.js',
774
			$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
775
		);
776
777
	// Do we need to add this setting?
778
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
779
780
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] . '/custom_avatar';
781
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
782
783
	// This little fellow has to cooperate...
784
	quickFileWritable($custom_av_dir);
785
786
	// Are we good now?
787
	if (!is_writable($custom_av_dir))
788
		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));
789
	elseif ($need_settings_update)
790
	{
791
		if (!function_exists('cache_put_data'))
792
			require_once($sourcedir . '/Load.php');
793
794
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
795
		updateSettings(array('custom_avatar_url' => $custom_av_url));
796
	}
797
798
	require_once($sourcedir . '/Security.php');
799
800
	// Check the cache directory.
801
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
802
	if (!file_exists($cachedir_temp))
803
		@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...
804
805
	if (!file_exists($cachedir_temp))
806
		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.');
807
808
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
809
		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>.');
810
	elseif (!isset($_GET['skiplang']))
811
	{
812
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
813
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
814
815
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
816
			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>]');
817
	}
818
819
	if (!makeFilesWritable($writable_files))
820
		return false;
821
822
	// Check agreement.txt. (it may not exist, in which case $boarddir must be writable.)
823 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
824
		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.');
825
826
	// Upgrade the agreement.
827
	elseif (isset($modSettings['agreement']))
828
	{
829
		$fp = fopen($boarddir . '/agreement.txt', 'w');
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $fp. Configured minimum length is 3.

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

Loading history...
830
		fwrite($fp, $modSettings['agreement']);
831
		fclose($fp);
832
	}
833
834
	// We're going to check that their board dir setting is right in case they've been moving stuff around.
835
	if (strtr($boarddir, array('/' => '', '\\' => '')) != strtr(dirname(__FILE__), array('/' => '', '\\' => '')))
836
		$upcontext['warning'] = '
837
			'. sprintf($txt['upgrade_boarddir_settings'], $boarddir, dirname(__FILE__)) .'<br>
838
			<ul>
839
				<li>'. $txt['upgrade_boarddir'] .'  ' . $boarddir . '</li>
840
				<li>'. $txt['upgrade_sourcedir'] .'  ' . $boarddir . '</li>
841
				<li>'. $txt['upgrade_cachedir'] .'  ' . $cachedir_temp . '</li>
842
			</ul>
843
			'. $txt['upgrade_incorrect_settings'] .'';
844
845
	// Confirm mbstring is loaded...
846
	if (!extension_loaded('mbstring'))
847
		return throw_error($txt['install_no_mbstring']);
848
849
	// Check for https stream support.
850
	$supported_streams = stream_get_wrappers();
851
	if (!in_array('https', $supported_streams))
852
		$upcontext['custom_warning'] = $txt['install_no_https'];
853
854
	// Either we're logged in or we're going to present the login.
855
	if (checkLogin())
856
		return true;
857
858
	$upcontext += createToken('login');
859
860
	return false;
861
}
862
863
// Step 0.5: Does the login work?
864
function checkLogin()
865
{
866
	global $modSettings, $upcontext, $disable_security;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
867
	global $smcFunc, $db_type, $support_js;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
868
869
	// Don't bother if the security is disabled.
870
	if ($disable_security)
871
		return true;
872
873
	// Are we trying to login?
874
	if (isset($_POST['contbutt']) && (!empty($_POST['user'])))
875
	{
876
		// If we've disabled security pick a suitable name!
877
		if (empty($_POST['user']))
878
			$_POST['user'] = 'Administrator';
879
880
		// Before 2.0 these column names were different!
881
		$oldDB = false;
882
		if (empty($db_type) || $db_type == 'mysql')
883
		{
884
			$request = $smcFunc['db_query']('', '
885
				SHOW COLUMNS
886
				FROM {db_prefix}members
887
				LIKE {string:member_name}',
888
				array(
889
					'member_name' => 'memberName',
890
					'db_error_skip' => true,
891
				)
892
			);
893
			if ($smcFunc['db_num_rows']($request) != 0)
894
				$oldDB = true;
895
			$smcFunc['db_free_result']($request);
896
		}
897
898
		// Get what we believe to be their details.
899
		if (!$disable_security)
900
		{
901
			if ($oldDB)
902
				$request = $smcFunc['db_query']('', '
903
					SELECT id_member, memberName AS member_name, passwd, id_group,
904
					additionalGroups AS additional_groups, lngfile
905
					FROM {db_prefix}members
906
					WHERE memberName = {string:member_name}',
907
					array(
908
						'member_name' => $_POST['user'],
909
						'db_error_skip' => true,
910
					)
911
				);
912
			else
913
				$request = $smcFunc['db_query']('', '
914
					SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile
915
					FROM {db_prefix}members
916
					WHERE member_name = {string:member_name}',
917
					array(
918
						'member_name' => $_POST['user'],
919
						'db_error_skip' => true,
920
					)
921
				);
922
			if ($smcFunc['db_num_rows']($request) != 0)
923
			{
924
				list ($id_member, $name, $password, $id_group, $addGroups, $user_language) = $smcFunc['db_fetch_row']($request);
925
926
				$groups = explode(',', $addGroups);
927
				$groups[] = $id_group;
928
929
				foreach ($groups as $k => $v)
930
					$groups[$k] = (int) $v;
931
932
				$sha_passwd = sha1(strtolower($name) . un_htmlspecialchars($_REQUEST['passwrd']));
933
934
				// We don't use "-utf8" anymore...
935
				$user_language = str_ireplace('-utf8', '', $user_language);
936
			}
937
			else
938
				$upcontext['username_incorrect'] = true;
939
940
			$smcFunc['db_free_result']($request);
941
		}
942
		$upcontext['username'] = $_POST['user'];
943
944
		// Track whether javascript works!
945
		if (!empty($_POST['js_works']))
946
		{
947
			$upcontext['upgrade_status']['js'] = 1;
948
			$support_js = 1;
949
		}
950
		else
951
			$support_js = 0;
952
953
		// Note down the version we are coming from.
954
		if (!empty($modSettings['smfVersion']) && empty($upcontext['user']['version']))
955
			$upcontext['user']['version'] = $modSettings['smfVersion'];
956
957
		// Didn't get anywhere?
958
		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...
959
		{
960
			// MD5?
961
			$md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user']));
962
			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...
963
			{
964
				$upcontext['password_failed'] = true;
965
				// Disable the hashing this time.
966
				$upcontext['disable_login_hashing'] = true;
967
			}
968
		}
969
970
		if ((empty($upcontext['password_failed']) && !empty($name)) || $disable_security)
971
		{
972
			// Set the password.
973
			if (!$disable_security)
974
			{
975
				// Do we actually have permission?
976
				if (!in_array(1, $groups))
977
				{
978
					$request = $smcFunc['db_query']('', '
979
						SELECT permission
980
						FROM {db_prefix}permissions
981
						WHERE id_group IN ({array_int:groups})
982
							AND permission = {string:admin_forum}',
983
						array(
984
							'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...
985
							'admin_forum' => 'admin_forum',
986
							'db_error_skip' => true,
987
						)
988
					);
989
					if ($smcFunc['db_num_rows']($request) == 0)
990
						return throw_error('You need to be an admin to perform an upgrade!');
991
					$smcFunc['db_free_result']($request);
992
				}
993
994
				$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...
995
				$upcontext['user']['name'] = $name;
996
			}
997
			else
998
			{
999
				$upcontext['user']['id'] = 1;
1000
				$upcontext['user']['name'] = 'Administrator';
1001
			}
1002
			$upcontext['user']['pass'] = mt_rand(0, 60000);
1003
			// This basically is used to match the GET variables to Settings.php.
1004
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
1005
1006
			// Set the language to that of the user?
1007
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
1008
			{
1009
				$user_language = basename($user_language, '.lng');
1010
				$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
1011
				preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
1012
1013
				if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
1014
					$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'] . '.';
1015
				elseif (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . basename($user_language, '.lng') . '.php'))
1016
					$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'] . '.';
1017
				else
1018
				{
1019
					// Set this as the new language.
1020
					$upcontext['language'] = $user_language;
1021
					$upcontext['upgrade_status']['lang'] = $upcontext['language'];
1022
1023
					// Include the file.
1024
					require_once($modSettings['theme_dir'] . '/languages/Install.' . $user_language . '.php');
1025
				}
1026
			}
1027
1028
			// If we're resuming set the step and substep to be correct.
1029
			if (isset($_POST['cont']))
1030
			{
1031
				$upcontext['current_step'] = $upcontext['user']['step'];
1032
				$_GET['substep'] = $upcontext['user']['substep'];
1033
			}
1034
1035
			return true;
1036
		}
1037
	}
1038
1039
	return false;
1040
}
1041
1042
// Step 1: Do the maintenance and backup.
1043
function UpgradeOptions()
1044
{
1045
	global $db_prefix, $command_line, $modSettings, $is_debug, $smcFunc, $packagesdir, $tasksdir, $language, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1046
	global $boarddir, $boardurl, $sourcedir, $maintenance, $cachedir, $upcontext, $db_type, $db_server;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1047
1048
	$upcontext['sub_template'] = 'upgrade_options';
1049
	$upcontext['page_title'] = $txt['upgrade_options'];
1050
1051
	db_extend('packages');
1052
	$upcontext['karma_installed'] = array('good' => false, 'bad' => false);
1053
	$member_columns = $smcFunc['db_list_columns']('{db_prefix}members');
1054
1055
	$upcontext['karma_installed']['good'] = in_array('karma_good', $member_columns);
1056
	$upcontext['karma_installed']['bad'] = in_array('karma_bad', $member_columns);
1057
1058
	unset($member_columns);
1059
1060
	// If we've not submitted then we're done.
1061
	if (empty($_POST['upcont']))
1062
		return false;
1063
1064
	// Firstly, if they're enabling SM stat collection just do it.
1065
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']))
1066
	{
1067
		$upcontext['allow_sm_stats'] = true;
1068
1069
		// Don't register if we still have a key.
1070
		if (empty($modSettings['sm_stats_key']))
1071
		{
1072
			// Attempt to register the site etc.
1073
			$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $fp. Configured minimum length is 3.

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

Loading history...
1074 View Code Duplication
			if ($fp)
1075
			{
1076
				$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
1077
				$out .= 'Host: www.simplemachines.org' . "\r\n";
1078
				$out .= 'Connection: Close' . "\r\n\r\n";
1079
				fwrite($fp, $out);
1080
1081
				$return_data = '';
1082
				while (!feof($fp))
1083
					$return_data .= fgets($fp, 128);
1084
1085
				fclose($fp);
1086
1087
				// Get the unique site ID.
1088
				preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ID. Configured minimum length is 3.

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

Loading history...
1089
1090
				if (!empty($ID[1]))
1091
					$smcFunc['db_insert']('replace',
1092
						$db_prefix . 'settings',
1093
						array('variable' => 'string', 'value' => 'string'),
1094
						array(
1095
							array('sm_stats_key', $ID[1]),
1096
							array('enable_sm_stats', 1),
1097
						),
1098
						array('variable')
1099
					);
1100
			}
1101
		}
1102
		else
1103
		{
1104
			$smcFunc['db_insert']('replace',
1105
				$db_prefix . 'settings',
1106
				array('variable' => 'string', 'value' => 'string'),
1107
				array('enable_sm_stats', 1),
1108
				array('variable')
1109
			);
1110
		}
1111
	}
1112
	// Don't remove stat collection unless we unchecked the box for real, not from the loop.
1113 View Code Duplication
	elseif (empty($_POST['stats']) && empty($upcontext['allow_sm_stats']))
1114
		$smcFunc['db_query']('', '
1115
			DELETE FROM {db_prefix}settings
1116
			WHERE variable = {string:enable_sm_stats}',
1117
			array(
1118
				'enable_sm_stats' => 'enable_sm_stats',
1119
				'db_error_skip' => true,
1120
			)
1121
		);
1122
1123
	// Deleting old karma stuff?
1124
	if (!empty($_POST['delete_karma']))
1125
	{
1126
		// Delete old settings vars.
1127
		$smcFunc['db_query']('', '
1128
			DELETE FROM {db_prefix}settings
1129
			WHERE variable IN ({array_string:karma_vars})',
1130
			array(
1131
				'karma_vars' => array('karmaMode', 'karmaTimeRestrictAdmins', 'karmaWaitTime', 'karmaMinPosts', 'karmaLabel', 'karmaSmiteLabel', 'karmaApplaudLabel'),
1132
			)
1133
		);
1134
1135
		// Cleaning up old karma member settings.
1136
		if ($upcontext['karma_installed']['good'])
1137
			$smcFunc['db_query']('', '
1138
				ALTER TABLE {db_prefix}members
1139
				DROP karma_good',
1140
				array()
1141
			);
1142
1143
		// Does karma bad was enable?
1144
		if ($upcontext['karma_installed']['bad'])
1145
			$smcFunc['db_query']('', '
1146
				ALTER TABLE {db_prefix}members
1147
				DROP karma_bad',
1148
				array()
1149
			);
1150
1151
		// Cleaning up old karma permissions.
1152
		$smcFunc['db_query']('', '
1153
			DELETE FROM {db_prefix}permissions
1154
			WHERE permission = {string:karma_vars}',
1155
			array(
1156
				'karma_vars' => 'karma_edit',
1157
			)
1158
		);
1159
		// Cleaning up old log_karma table
1160
		$smcFunc['db_query']('', '
1161
			DROP TABLE IF EXISTS {db_prefix}log_karma',
1162
			array()
1163
		);
1164
	}
1165
1166
	// Emptying the error log?
1167
	if (!empty($_POST['empty_error']))
1168
		$smcFunc['db_query']('truncate_table', '
1169
			TRUNCATE {db_prefix}log_errors',
1170
			array(
1171
			)
1172
		);
1173
1174
	$changes = array();
1175
1176
	// Add proxy settings.
1177
	if (!isset($GLOBALS['image_proxy_maxsize']))
1178
		$changes += array(
1179
			'image_proxy_secret' => '\'' . substr(sha1(mt_rand()), 0, 20) . '\'',
1180
			'image_proxy_maxsize' => 5190,
1181
			'image_proxy_enabled' => 0,
1182
		);
1183
1184
	// If $boardurl reflects https, set force_ssl
1185
	if (!function_exists('cache_put_data'))
1186
		require_once($sourcedir . '/Load.php');
1187
	if (stripos($boardurl, 'https://') !== false)
1188
		updateSettings(array('force_ssl' => '1'));
1189
1190
	// If we're overriding the language follow it through.
1191 View Code Duplication
	if (isset($_GET['lang']) && file_exists($modSettings['theme_dir'] . '/languages/index.' . $_GET['lang'] . '.php'))
1192
		$changes['language'] = '\'' . $_GET['lang'] . '\'';
1193
1194
	if (!empty($_POST['maint']))
1195
	{
1196
		$changes['maintenance'] = '2';
1197
		// Remember what it was...
1198
		$upcontext['user']['main'] = $maintenance;
1199
1200
		if (!empty($_POST['maintitle']))
1201
		{
1202
			$changes['mtitle'] = '\'' . addslashes($_POST['maintitle']) . '\'';
1203
			$changes['mmessage'] = '\'' . addslashes($_POST['mainmessage']) . '\'';
1204
		}
1205
		else
1206
		{
1207
			$changes['mtitle'] = '\'Upgrading the forum...\'';
1208
			$changes['mmessage'] = '\'Don\\\'t worry, we will be back shortly with an updated forum.  It will only be a minute ;).\'';
1209
		}
1210
	}
1211
1212
	if ($command_line)
1213
		echo ' * Updating Settings.php...';
1214
1215
	// Fix some old paths.
1216 View Code Duplication
	if (substr($boarddir, 0, 1) == '.')
1217
		$changes['boarddir'] = '\'' . fixRelativePath($boarddir) . '\'';
1218
1219 View Code Duplication
	if (substr($sourcedir, 0, 1) == '.')
1220
		$changes['sourcedir'] = '\'' . fixRelativePath($sourcedir) . '\'';
1221
1222
	if (empty($cachedir) || substr($cachedir, 0, 1) == '.')
1223
		$changes['cachedir'] = '\'' . fixRelativePath($boarddir) . '/cache\'';
1224
1225
	// If they have a "host:port" setup for the host, split that into separate values
1226
	// You should never have a : in the hostname if you're not on MySQL, but better safe than sorry
1227
	if (strpos($db_server, ':') !== false && $db_type == 'mysql')
1228
	{
1229
		list ($db_server, $db_port) = explode(':', $db_server);
1230
1231
		$changes['db_server'] = '\'' . $db_server . '\'';
1232
1233
		// Only set this if we're not using the default port
1234
		if ($db_port != ini_get('mysqli.default_port'))
1235
			$changes['db_port'] = (int) $db_port;
1236
	}
1237
	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...
1238
	{
1239
		// If db_port is set and is the same as the default, set it to ''
1240
		if ($db_type == 'mysql')
1241
		{
1242
			if ($db_port == ini_get('mysqli.default_port'))
1243
				$changes['db_port'] = '\'\'';
1244
			elseif ($db_type == 'postgresql' && $db_port == 5432)
1245
				$changes['db_port'] = '\'\'';
1246
		}
1247
	}
1248
1249
	// Maybe we haven't had this option yet?
1250
	if (empty($packagesdir))
1251
		$changes['packagesdir'] = '\'' . fixRelativePath($boarddir) . '/Packages\'';
1252
1253
	// Add support for $tasksdir var.
1254
	if (empty($tasksdir))
1255
		$changes['tasksdir'] = '\'' . fixRelativePath($sourcedir) . '/tasks\'';
1256
1257
	// Make sure we fix the language as well.
1258
	if (stristr($language, '-utf8'))
1259
		$changes['language'] = '\'' . str_ireplace('-utf8', '', $language) . '\'';
1260
1261
	// @todo Maybe change the cookie name if going to 1.1, too?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
1262
1263
	// Update Settings.php with the new settings.
1264
	require_once($sourcedir . '/Subs-Admin.php');
1265
	updateSettingsFile($changes);
1266
1267
	// Tell Settings.php to store db_last_error.php in the cache
1268
	move_db_last_error_to_cachedir();
1269
1270
	if ($command_line)
1271
		echo ' Successful.' . "\n";
1272
1273
	// Are we doing debug?
1274
	if (isset($_POST['debug']))
1275
	{
1276
		$upcontext['upgrade_status']['debug'] = true;
1277
		$is_debug = true;
1278
	}
1279
1280
	// If we're not backing up then jump one.
1281
	if (empty($_POST['backup']))
1282
		$upcontext['current_step']++;
1283
1284
	// If we've got here then let's proceed to the next step!
1285
	return true;
1286
}
1287
1288
// Backup the database - why not...
1289
function BackupDatabase()
1290
{
1291
	global $upcontext, $db_prefix, $command_line, $support_js, $file_steps, $smcFunc, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1292
1293
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1294
	$upcontext['page_title'] = $txt['backup_database'];
1295
1296
	// Done it already - js wise?
1297
	if (!empty($_POST['backup_done']))
1298
		return true;
1299
1300
	// Some useful stuff here.
1301
	db_extend();
1302
1303
	// Might need this as well
1304
	db_extend('packages');
1305
1306
	// Get all the table names.
1307
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1308
	$db = preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? strtr($match[1], array('`' => '')) : false;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $db. Configured minimum length is 3.

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

Loading history...
1309
	$tables = $smcFunc['db_list_tables']($db, $filter);
1310
1311
	$table_names = array();
1312
	foreach ($tables as $table)
1313
		if (substr($table, 0, 7) !== 'backup_')
1314
			$table_names[] = $table;
1315
1316
	$upcontext['table_count'] = count($table_names);
1317
	$upcontext['cur_table_num'] = $_GET['substep'];
1318
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1319
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1320
	// For non-java auto submit...
1321
	$file_steps = $upcontext['table_count'];
1322
1323
	// What ones have we already done?
1324 View Code Duplication
	foreach ($table_names as $id => $table)
1325
		if ($id < $_GET['substep'])
1326
			$upcontext['previous_tables'][] = $table;
1327
1328
	if ($command_line)
1329
		echo 'Backing Up Tables.';
1330
1331
	// If we don't support javascript we backup here.
1332
	if (!$support_js || isset($_GET['xml']))
1333
	{
1334
		// Backup each table!
1335
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1336
		{
1337
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1338
			$upcontext['cur_table_num'] = $substep + 1;
1339
1340
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1341
1342
			// Do we need to pause?
1343
			nextSubstep($substep);
1344
1345
			backupTable($table_names[$substep]);
1346
1347
			// If this is XML to keep it nice for the user do one table at a time anyway!
1348
			if (isset($_GET['xml']))
1349
				return upgradeExit();
1350
		}
1351
1352
		if ($command_line)
1353
		{
1354
			echo "\n" . ' Successful.\'' . "\n";
1355
			flush();
1356
		}
1357
		$upcontext['step_progress'] = 100;
1358
1359
		$_GET['substep'] = 0;
1360
		// Make sure we move on!
1361
		return true;
1362
	}
1363
1364
	// Either way next place to post will be database changes!
1365
	$_GET['substep'] = 0;
1366
	return false;
1367
}
1368
1369
// Backup one table...
1370
function backupTable($table)
1371
{
1372
	global $command_line, $db_prefix, $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1373
1374
	if ($command_line)
1375
	{
1376
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1377
		flush();
1378
	}
1379
1380
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1381
1382
	if ($command_line)
1383
		echo ' done.';
1384
}
1385
1386
// Step 2: Everything.
1387
function DatabaseChanges()
1388
{
1389
	global $db_prefix, $modSettings, $smcFunc, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1390
	global $upcontext, $support_js, $db_type;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1391
1392
	// Have we just completed this?
1393
	if (!empty($_POST['database_done']))
1394
		return true;
1395
1396
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1397
	$upcontext['page_title'] = $txt['database_changes'];
1398
1399
	// All possible files.
1400
	// Name, < version, insert_on_complete
1401
	$files = array(
1402
		array('upgrade_1-0.sql', '1.1', '1.1 RC0'),
1403
		array('upgrade_1-1.sql', '2.0', '2.0 a'),
1404
		array('upgrade_2-0_' . $db_type . '.sql', '2.1', '2.1 dev0'),
1405
		array('upgrade_2-1_' . $db_type . '.sql', '3.0', SMF_VERSION),
1406
	);
1407
1408
	// How many files are there in total?
1409
	if (isset($_GET['filecount']))
1410
		$upcontext['file_count'] = (int) $_GET['filecount'];
1411
	else
1412
	{
1413
		$upcontext['file_count'] = 0;
1414
		foreach ($files as $file)
1415
		{
1416
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1417
				$upcontext['file_count']++;
1418
		}
1419
	}
1420
1421
	// Do each file!
1422
	$did_not_do = count($files) - $upcontext['file_count'];
1423
	$upcontext['step_progress'] = 0;
1424
	$upcontext['cur_file_num'] = 0;
1425
	foreach ($files as $file)
1426
	{
1427
		if ($did_not_do)
1428
			$did_not_do--;
1429
		else
1430
		{
1431
			$upcontext['cur_file_num']++;
1432
			$upcontext['cur_file_name'] = $file[0];
1433
			// Do we actually need to do this still?
1434
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1435
			{
1436
				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1437
				if ($nextFile)
1438
				{
1439
					// Only update the version of this if complete.
1440
					$smcFunc['db_insert']('replace',
1441
						$db_prefix . 'settings',
1442
						array('variable' => 'string', 'value' => 'string'),
1443
						array('smfVersion', $file[2]),
1444
						array('variable')
1445
					);
1446
1447
					$modSettings['smfVersion'] = $file[2];
1448
				}
1449
1450
				// If this is XML we only do this stuff once.
1451
				if (isset($_GET['xml']))
1452
				{
1453
					// Flag to move on to the next.
1454
					$upcontext['completed_step'] = true;
1455
					// Did we complete the whole file?
1456
					if ($nextFile)
1457
						$upcontext['current_debug_item_num'] = -1;
1458
					return upgradeExit();
1459
				}
1460
				elseif ($support_js)
1461
					break;
1462
			}
1463
			// Set the progress bar to be right as if we had - even if we hadn't...
1464
			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1465
		}
1466
	}
1467
1468
	$_GET['substep'] = 0;
1469
	// So the template knows we're done.
1470
	if (!$support_js)
1471
	{
1472
		$upcontext['changes_complete'] = true;
1473
1474
		return true;
1475
	}
1476
	return false;
1477
}
1478
1479
1480
// Delete the damn thing!
1481
function DeleteUpgrade()
1482
{
1483
	global $command_line, $language, $upcontext, $sourcedir, $forum_version;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1484
	global $user_info, $maintenance, $smcFunc, $db_type, $txt, $settings;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1485
1486
	// Now it's nice to have some of the basic SMF source files.
1487
	if (!isset($_GET['ssi']) && !$command_line)
1488
		redirectLocation('&ssi=1');
1489
1490
	$upcontext['sub_template'] = 'upgrade_complete';
1491
	$upcontext['page_title'] = $txt['upgrade_complete'];
1492
1493
	$endl = $command_line ? "\n" : '<br>' . "\n";
1494
1495
	$changes = array(
1496
		'language' => '\'' . (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language) . '\'',
1497
		'db_error_send' => '1',
1498
		'upgradeData' => '\'\'',
1499
	);
1500
1501
	// Are we in maintenance mode?
1502
	if (isset($upcontext['user']['main']))
1503
	{
1504
		if ($command_line)
1505
			echo ' * ';
1506
		$upcontext['removed_maintenance'] = true;
1507
		$changes['maintenance'] = $upcontext['user']['main'];
1508
	}
1509
	// Otherwise if somehow we are in 2 let's go to 1.
1510
	elseif (!empty($maintenance) && $maintenance == 2)
1511
		$changes['maintenance'] = 1;
1512
1513
	// Wipe this out...
1514
	$upcontext['user'] = array();
1515
1516
	require_once($sourcedir . '/Subs-Admin.php');
1517
	updateSettingsFile($changes);
1518
1519
	// Clean any old cache files away.
1520
	upgrade_clean_cache();
1521
1522
	// Can we delete the file?
1523
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1524
1525
	// Now is the perfect time to fetch the SM files.
1526
	if ($command_line)
1527
		cli_scheduled_fetchSMfiles();
1528
	else
1529
	{
1530
		require_once($sourcedir . '/ScheduledTasks.php');
1531
		$forum_version = SMF_VERSION; // The variable is usually defined in index.php so lets just use the constant to do it for us.
1532
		scheduled_fetchSMfiles(); // Now go get those files!
1533
		// This is needed in case someone invokes the upgrader using https when upgrading an http forum
1534 View Code Duplication
		if (httpsOn())
1535
			$settings['default_theme_url'] = strtr($settings['default_theme_url'], array('http://' => 'https://'));
1536
	}
1537
1538
	// Log what we've done.
1539
	if (empty($user_info['id']))
1540
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
1541
1542
	// Log the action manually, so CLI still works.
1543
	$smcFunc['db_insert']('',
1544
		'{db_prefix}log_actions',
1545
		array(
1546
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'inet', 'action' => 'string',
1547
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
1548
		),
1549
		array(
1550
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
1551
			0, 0, 0, json_encode(array('version' => $forum_version, 'member' => $user_info['id'])),
1552
		),
1553
		array('id_action')
1554
	);
1555
	$user_info['id'] = 0;
1556
1557
	// Save the current database version.
1558
	$server_version = $smcFunc['db_server_info']();
1559 View Code Duplication
	if ($db_type == 'mysql' && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51')))
1560
		updateSettings(array('db_mysql_group_by_fix' => '1'));
1561
1562
	if ($command_line)
1563
	{
1564
		echo $endl;
1565
		echo 'Upgrade Complete!', $endl;
1566
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
1567
		exit;
1568
	}
1569
1570
	// Make sure it says we're done.
1571
	$upcontext['overall_percent'] = 100;
1572
	if (isset($upcontext['step_progress']))
1573
		unset($upcontext['step_progress']);
1574
1575
	$_GET['substep'] = 0;
1576
	return false;
1577
}
1578
1579
// Just like the built in one, but setup for CLI to not use themes.
1580
function cli_scheduled_fetchSMfiles()
1581
{
1582
	global $sourcedir, $language, $forum_version, $modSettings, $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1583
1584
	if (empty($modSettings['time_format']))
1585
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1586
1587
	// What files do we want to get
1588
	$request = $smcFunc['db_query']('', '
1589
		SELECT id_file, filename, path, parameters
1590
		FROM {db_prefix}admin_info_files',
1591
		array(
1592
		)
1593
	);
1594
1595
	$js_files = array();
1596 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
1597
	{
1598
		$js_files[$row['id_file']] = array(
1599
			'filename' => $row['filename'],
1600
			'path' => $row['path'],
1601
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode($forum_version)),
1602
		);
1603
	}
1604
	$smcFunc['db_free_result']($request);
1605
1606
	// We're gonna need fetch_web_data() to pull this off.
1607
	require_once($sourcedir . '/Subs.php');
1608
1609
	foreach ($js_files as $ID_FILE => $file)
1610
	{
1611
		// Create the url
1612
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'https://www.simplemachines.org' : '';
1613
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1614
1615
		// Get the file
1616
		$file_data = fetch_web_data($url);
1617
1618
		// If we got an error - give up - the site might be down.
1619
		if ($file_data === false)
1620
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
1621
1622
		// Save the file to the database.
1623
		$smcFunc['db_query']('substring', '
1624
			UPDATE {db_prefix}admin_info_files
1625
			SET data = SUBSTRING({string:file_data}, 1, 65534)
1626
			WHERE id_file = {int:id_file}',
1627
			array(
1628
				'id_file' => $ID_FILE,
1629
				'file_data' => $file_data,
1630
			)
1631
		);
1632
	}
1633
	return true;
1634
}
1635
1636
function convertSettingsToTheme()
1637
{
1638
	global $db_prefix, $modSettings, $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1639
1640
	$values = array(
1641
		'show_latest_member' => @$GLOBALS['showlatestmember'],
1642
		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
1643
		'show_modify' => @$GLOBALS['showmodify'],
1644
		'show_user_images' => @$GLOBALS['showuserpic'],
1645
		'show_blurb' => @$GLOBALS['showusertext'],
1646
		'show_gender' => @$GLOBALS['showgenderimage'],
1647
		'show_newsfader' => @$GLOBALS['shownewsfader'],
1648
		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
1649
		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
1650
		'linktree_link' => @$GLOBALS['curposlinks'],
1651
		'show_profile_buttons' => @$GLOBALS['profilebutton'],
1652
		'show_mark_read' => @$GLOBALS['showmarkread'],
1653
		'newsfader_time' => @$GLOBALS['fadertime'],
1654
		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
1655
		'enable_news' => @$GLOBALS['enable_news'],
1656
		'return_to_post' => @$modSettings['returnToPost'],
1657
	);
1658
1659
	$themeData = array();
1660
	foreach ($values as $variable => $value)
1661
	{
1662
		if (!isset($value) || $value === null)
1663
			$value = 0;
1664
1665
		$themeData[] = array(0, 1, $variable, $value);
1666
	}
1667 View Code Duplication
	if (!empty($themeData))
1668
	{
1669
		$smcFunc['db_insert']('ignore',
1670
			$db_prefix . 'themes',
1671
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
1672
			$themeData,
1673
			array('id_member', 'id_theme', 'variable')
1674
		);
1675
	}
1676
}
1677
1678
// This function only works with MySQL but that's fine as it is only used for v1.0.
1679
function convertSettingstoOptions()
1680
{
1681
	global $modSettings, $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1682
1683
	// Format: new_setting -> old_setting_name.
1684
	$values = array(
1685
		'calendar_start_day' => 'cal_startmonday',
1686
		'view_newest_first' => 'viewNewestFirst',
1687
		'view_newest_pm_first' => 'viewNewestFirst',
1688
	);
1689
1690
	foreach ($values as $variable => $value)
1691
	{
1692
		if (empty($modSettings[$value[0]]))
1693
			continue;
1694
1695
		$smcFunc['db_query']('', '
1696
			INSERT IGNORE INTO {db_prefix}themes
1697
				(id_member, id_theme, variable, value)
1698
			SELECT id_member, 1, {string:variable}, {string:value}
1699
			FROM {db_prefix}members',
1700
			array(
1701
				'variable' => $variable,
1702
				'value' => $modSettings[$value[0]],
1703
				'db_error_skip' => true,
1704
			)
1705
		);
1706
1707
		$smcFunc['db_query']('', '
1708
			INSERT IGNORE INTO {db_prefix}themes
1709
				(id_member, id_theme, variable, value)
1710
			VALUES (-1, 1, {string:variable}, {string:value})',
1711
			array(
1712
				'variable' => $variable,
1713
				'value' => $modSettings[$value[0]],
1714
				'db_error_skip' => true,
1715
			)
1716
		);
1717
	}
1718
}
1719
1720
function php_version_check()
1721
{
1722
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
1723
}
1724
1725
function db_version_check()
1726
{
1727
	global $db_type, $databases;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1728
1729
	$curver = eval($databases[$db_type]['version_check']);
0 ignored issues
show
Coding Style introduced by
The function db_version_check() contains an eval expression.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
1730
	$curver = preg_replace('~\-.+?$~', '', $curver);
1731
1732
	return version_compare($databases[$db_type]['version'], $curver, '<=');
1733
}
1734
1735
function fixRelativePath($path)
1736
{
1737
	global $install_path;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1738
1739
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
1740
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
1741
}
1742
1743
function parse_sql($filename)
1744
{
1745
	global $db_prefix, $db_collation, $boarddir, $boardurl, $command_line, $file_steps, $step_progress, $custom_warning;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1746
	global $upcontext, $support_js, $is_debug, $db_type, $db_character_set;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1747
1748
/*
1749
	Failure allowed on:
1750
		- INSERT INTO but not INSERT IGNORE INTO.
1751
		- UPDATE IGNORE but not UPDATE.
1752
		- ALTER TABLE and ALTER IGNORE TABLE.
1753
		- DROP TABLE.
1754
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
1755
1756
	If a comment...
1757
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
1758
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
1759
		- is only ---#, it is "done." and then a break - only shown in debug.
1760
		- begins with ---{ it is a code block terminating at ---}.
1761
1762
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
1763
1764
	Replaces the following variables:
1765
		- {$boarddir}
1766
		- {$boardurl}
1767
		- {$db_prefix}
1768
		- {$db_collation}
1769
*/
1770
1771
	// May want to use extended functionality.
1772
	db_extend();
1773
	db_extend('packages');
1774
1775
	// Our custom error handler - does nothing but does stop public errors from XML!
1776
	set_error_handler(
1777
		function ($errno, $errstr, $errfile, $errline) use ($support_js)
1778
		{
1779
			if ($support_js)
1780
				return true;
1781
			else
1782
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
1783
		}
1784
	);
1785
1786
	// If we're on MySQL, set {db_collation}; this approach is used throughout upgrade_2-0_mysql.php to set new tables to utf8
1787
	// Note it is expected to be in the format: ENGINE=MyISAM{$db_collation};
1788
	if ($db_type == 'mysql')
1789
		$db_collation = ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1790
	else
1791
		$db_collation = '';
1792
1793
	$endl = $command_line ? "\n" : '<br>' . "\n";
1794
1795
	$lines = file($filename);
1796
1797
	$current_type = 'sql';
1798
	$current_data = '';
1799
	$substep = 0;
1800
	$last_step = '';
1801
1802
	// Make sure all newly created tables will have the proper characters set; this approach is used throughout upgrade_2-1_mysql.php
1803
	if (isset($db_character_set) && $db_character_set === 'utf8')
1804
		$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
1805
1806
	// Count the total number of steps within this file - for progress.
1807
	$file_steps = substr_count(implode('', $lines), '---#');
1808
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
1809
	$upcontext['debug_items'] = $file_steps;
1810
	$upcontext['current_item_num'] = 0;
1811
	$upcontext['current_item_name'] = '';
1812
	$upcontext['current_debug_item_num'] = 0;
1813
	$upcontext['current_debug_item_name'] = '';
1814
	// This array keeps a record of what we've done in case java is dead...
1815
	$upcontext['actioned_items'] = array();
1816
1817
	$done_something = false;
1818
1819
	foreach ($lines as $line_number => $line)
1820
	{
1821
		$do_current = $substep >= $_GET['substep'];
1822
1823
		// Get rid of any comments in the beginning of the line...
1824
		if (substr(trim($line), 0, 2) === '/*')
1825
			$line = preg_replace('~/\*.+?\*/~', '', $line);
1826
1827
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
1828
		if ($is_debug && !$support_js && $command_line)
1829
			flush();
1830
1831
		if (trim($line) === '')
1832
			continue;
1833
1834
		if (trim(substr($line, 0, 3)) === '---')
1835
		{
1836
			$type = substr($line, 3, 1);
1837
1838
			// An error??
1839
			if (trim($current_data) != '' && $type !== '}')
1840
			{
1841
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
1842
				if ($command_line)
1843
					echo $upcontext['error_message'];
1844
			}
1845
1846
			if ($type == ' ')
1847
			{
1848
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
1849
				{
1850
					echo ' Successful.', $endl;
1851
					flush();
1852
				}
1853
1854
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
1855
				$upcontext['current_item_num']++;
1856
				$upcontext['current_item_name'] = $last_step;
1857
1858
				if ($do_current)
1859
				{
1860
					$upcontext['actioned_items'][] = $last_step;
1861
					if ($command_line)
1862
						echo ' * ';
1863
				}
1864
			}
1865
			elseif ($type == '#')
1866
			{
1867
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
1868
1869
				$upcontext['current_debug_item_num']++;
1870
				if (trim($line) != '---#')
1871
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
1872
1873
				// Have we already done something?
1874
				if (isset($_GET['xml']) && $done_something)
1875
				{
1876
					restore_error_handler();
1877
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
1878
				}
1879
1880
				if ($do_current)
1881
				{
1882
					if (trim($line) == '---#' && $command_line)
1883
						echo ' done.', $endl;
1884
					elseif ($command_line)
1885
						echo ' +++ ', rtrim(substr($line, 4));
1886
					elseif (trim($line) != '---#')
1887
					{
1888
						if ($is_debug)
1889
							$upcontext['actioned_items'][] = htmlspecialchars(rtrim(substr($line, 4)));
1890
					}
1891
				}
1892
1893
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
1894
				{
1895
					if ($command_line)
1896
						echo ' * ';
1897
					else
1898
						$upcontext['actioned_items'][] = $last_step;
1899
				}
1900
1901
				// Small step - only if we're actually doing stuff.
1902
				if ($do_current)
1903
					nextSubstep(++$substep);
1904
				else
1905
					$substep++;
1906
			}
1907
			elseif ($type == '{')
1908
				$current_type = 'code';
1909
			elseif ($type == '}')
1910
			{
1911
				$current_type = 'sql';
1912
1913
				if (!$do_current)
1914
				{
1915
					$current_data = '';
1916
					continue;
1917
				}
1918
1919
				if (eval('global $db_prefix, $modSettings, $smcFunc; ' . $current_data) === false)
0 ignored issues
show
Coding Style introduced by
The function parse_sql() contains an eval expression.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
1920
				{
1921
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
1922
					if ($command_line)
1923
						echo $upcontext['error_message'];
1924
				}
1925
1926
				// Done with code!
1927
				$current_data = '';
1928
				$done_something = true;
1929
			}
1930
1931
			continue;
1932
		}
1933
1934
		$current_data .= $line;
1935
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
1936
		{
1937
			if ((!$support_js || isset($_GET['xml'])))
1938
			{
1939
				if (!$do_current)
1940
				{
1941
					$current_data = '';
1942
					continue;
1943
				}
1944
1945
				$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));
1946
1947
				upgrade_query($current_data);
1948
1949
				// @todo This will be how it kinda does it once mysql all stripped out - needed for postgre (etc).
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
1950
				/*
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1951
				$result = $smcFunc['db_query']('', $current_data, false, false);
1952
				// Went wrong?
1953
				if (!$result)
1954
				{
1955
					// Bit of a bodge - do we want the error?
1956
					if (!empty($upcontext['return_error']))
1957
					{
1958
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
1959
						return false;
1960
					}
1961
				}*/
1962
				$done_something = true;
1963
			}
1964
			$current_data = '';
1965
		}
1966
		// If this is xml based and we're just getting the item name then that's grand.
1967
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
1968
		{
1969
			restore_error_handler();
1970
			return false;
1971
		}
1972
1973
		// Clean up by cleaning any step info.
1974
		$step_progress = array();
1975
		$custom_warning = '';
1976
	}
1977
1978
	// Put back the error handler.
1979
	restore_error_handler();
1980
1981
	if ($command_line)
1982
	{
1983
		echo ' Successful.' . "\n";
1984
		flush();
1985
	}
1986
1987
	$_GET['substep'] = 0;
1988
	return true;
1989
}
1990
1991
function upgrade_query($string, $unbuffered = false)
1992
{
1993
	global $db_connection, $db_server, $db_user, $db_passwd, $db_type, $command_line, $upcontext, $upgradeurl, $modSettings;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1994
	global $db_name, $db_unbuffered, $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1995
1996
	// Get the query result - working around some SMF specific security - just this once!
1997
	$modSettings['disableQueryCheck'] = true;
1998
	$db_unbuffered = $unbuffered;
1999
	$ignore_insert_error = false;
2000
2001
	// If we got an old pg version and use a insert ignore query
2002
	if ($db_type == 'postgresql' && !$smcFunc['db_native_replace']() && strpos($string, 'ON CONFLICT DO NOTHING') !== false)
2003
	{
2004
		$ignore_insert_error = true;
2005
		$string = str_replace('ON CONFLICT DO NOTHING', '', $string);
2006
	}
2007
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
2008
	$db_unbuffered = false;
2009
2010
	// Failure?!
2011
	if ($result !== false)
2012
		return $result;
2013
2014
	$db_error_message = $smcFunc['db_error']($db_connection);
2015
	// If MySQL we do something more clever.
2016
	if ($db_type == 'mysql')
2017
	{
2018
		$mysqli_errno = mysqli_errno($db_connection);
2019
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR'));
2020
2021
		// Error numbers:
2022
		//    1016: Can't open file '....MYI'
2023
		//    1050: Table already exists.
2024
		//    1054: Unknown column name.
2025
		//    1060: Duplicate column name.
2026
		//    1061: Duplicate key name.
2027
		//    1062: Duplicate entry for unique key.
2028
		//    1068: Multiple primary keys.
2029
		//    1072: Key column '%s' doesn't exist in table.
2030
		//    1091: Can't drop key, doesn't exist.
2031
		//    1146: Table doesn't exist.
2032
		//    2013: Lost connection to server during query.
2033
2034
		if ($mysqli_errno == 1016)
2035
		{
2036
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
2037
			{
2038
				mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
2039
				$result = mysqli_query($db_connection, $string);
2040
				if ($result !== false)
2041
					return $result;
2042
			}
2043
		}
2044
		elseif ($mysqli_errno == 2013)
2045
		{
2046
			$db_connection = mysqli_connect($db_server, $db_user, $db_passwd);
2047
			mysqli_select_db($db_connection, $db_name);
2048
			if ($db_connection)
2049
			{
2050
				$result = mysqli_query($db_connection, $string);
2051
				if ($result !== false)
2052
					return $result;
2053
			}
2054
		}
2055
		// Duplicate column name... should be okay ;).
2056 View Code Duplication
		elseif (in_array($mysqli_errno, array(1060, 1061, 1068, 1091)))
2057
			return false;
2058
		// Duplicate insert... make sure it's the proper type of query ;).
2059 View Code Duplication
		elseif (in_array($mysqli_errno, array(1054, 1062, 1146)) && $error_query)
2060
			return false;
2061
		// Creating an index on a non-existent column.
2062
		elseif ($mysqli_errno == 1072)
2063
			return false;
2064
		elseif ($mysqli_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
2065
			return false;
2066
	}
2067
	// If a table already exists don't go potty.
2068
	else
2069
	{
2070
		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U')))
2071
		{
2072
			if (strpos($db_error_message, 'exist') !== false)
2073
				return true;
2074
		}
2075
		elseif (strpos(trim($string), 'INSERT ') !== false)
2076
		{
2077
			if (strpos($db_error_message, 'duplicate') !== false || $ignore_insert_error)
2078
				return true;
2079
		}
2080
	}
2081
2082
	// Get the query string so we pass everything.
2083
	$query_string = '';
2084
	foreach ($_GET as $k => $v)
2085
		$query_string .= ';' . $k . '=' . $v;
2086
	if (strlen($query_string) != 0)
2087
		$query_string = '?' . substr($query_string, 1);
2088
2089
	if ($command_line)
2090
	{
2091
		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
2092
		die;
2093
	}
2094
2095
	// Bit of a bodge - do we want the error?
2096
	if (!empty($upcontext['return_error']))
2097
	{
2098
		$upcontext['error_message'] = $db_error_message;
2099
		$upcontext['error_string'] = $string;
2100
		return false;
2101
	}
2102
2103
	// Otherwise we have to display this somewhere appropriate if possible.
2104
	$upcontext['forced_error_message'] = '
2105
			<strong>'. $txt['upgrade_unsuccessful'] .'</strong><br>
0 ignored issues
show
Bug introduced by
The variable $txt 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...
2106
2107
			<div style="margin: 2ex;">
2108
				'. $txt['upgrade_thisquery'] .'
2109
				<blockquote><pre>' . nl2br(htmlspecialchars(trim($string))) . ';</pre></blockquote>
2110
2111
				'. $txt['upgrade_causerror'] .'
2112
				<blockquote>' . nl2br(htmlspecialchars($db_error_message)) . '</blockquote>
2113
			</div>
2114
2115
			<form action="' . $upgradeurl . $query_string . '" method="post">
2116
				<input type="submit" value="Try again" class="button">
2117
			</form>
2118
		</div>';
2119
2120
	upgradeExit();
2121
}
2122
2123
// This performs a table alter, but does it unbuffered so the script can time out professionally.
2124
function protected_alter($change, $substep, $is_test = false)
2125
{
2126
	global $db_prefix, $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2127
2128
	db_extend('packages');
2129
2130
	// Firstly, check whether the current index/column exists.
2131
	$found = false;
2132
	if ($change['type'] === 'column')
2133
	{
2134
		$columns = $smcFunc['db_list_columns']('{db_prefix}' . $change['table'], true);
2135
		foreach ($columns as $column)
2136
		{
2137
			// Found it?
2138
			if ($column['name'] === $change['name'])
2139
			{
2140
				$found |= 1;
2141
				// Do some checks on the data if we have it set.
2142
				if (isset($change['col_type']))
2143
					$found &= $change['col_type'] === $column['type'];
2144
				if (isset($change['null_allowed']))
2145
					$found &= $column['null'] == $change['null_allowed'];
2146
				if (isset($change['default']))
2147
					$found &= $change['default'] === $column['default'];
2148
			}
2149
		}
2150
	}
2151
	elseif ($change['type'] === 'index')
2152
	{
2153
		$request = upgrade_query('
2154
			SHOW INDEX
2155
			FROM ' . $db_prefix . $change['table']);
2156
		if ($request !== false)
2157
		{
2158
			$cur_index = array();
2159
2160
			while ($row = $smcFunc['db_fetch_assoc']($request))
2161
				if ($row['Key_name'] === $change['name'])
2162
					$cur_index[(int) $row['Seq_in_index']] = $row['Column_name'];
2163
2164
			ksort($cur_index, SORT_NUMERIC);
2165
			$found = array_values($cur_index) === $change['target_columns'];
2166
2167
			$smcFunc['db_free_result']($request);
2168
		}
2169
	}
2170
2171
	// If we're trying to add and it's added, we're done.
2172
	if ($found && in_array($change['method'], array('add', 'change')))
2173
		return true;
2174
	// Otherwise if we're removing and it wasn't found we're also done.
2175
	elseif (!$found && in_array($change['method'], array('remove', 'change_remove')))
2176
		return true;
2177
	// Otherwise is it just a test?
2178
	elseif ($is_test)
2179
		return false;
2180
2181
	// Not found it yet? Bummer! How about we see if we're currently doing it?
2182
	$running = false;
2183
	$found = false;
2184
	while (1 == 1)
2185
	{
2186
		$request = upgrade_query('
2187
			SHOW FULL PROCESSLIST');
2188
		while ($row = $smcFunc['db_fetch_assoc']($request))
2189
		{
2190
			if (strpos($row['Info'], 'ALTER TABLE ' . $db_prefix . $change['table']) !== false && strpos($row['Info'], $change['text']) !== false)
2191
				$found = true;
2192
		}
2193
2194
		// Can't find it? Then we need to run it fools!
2195
		if (!$found && !$running)
2196
		{
2197
			$smcFunc['db_free_result']($request);
2198
2199
			$success = upgrade_query('
2200
				ALTER TABLE ' . $db_prefix . $change['table'] . '
2201
				' . $change['text'], true) !== false;
2202
2203
			if (!$success)
2204
				return false;
2205
2206
			// Return
2207
			$running = true;
2208
		}
2209
		// What if we've not found it, but we'd ran it already? Must of completed.
2210
		elseif (!$found)
2211
		{
2212
			$smcFunc['db_free_result']($request);
2213
			return true;
2214
		}
2215
2216
		// Pause execution for a sec or three.
2217
		sleep(3);
2218
2219
		// Can never be too well protected.
2220
		nextSubstep($substep);
2221
	}
2222
2223
	// Protect it.
2224
	nextSubstep($substep);
2225
}
2226
2227
/**
2228
 * Alter a text column definition preserving its character set.
2229
 *
2230
 * @param array $change
2231
 * @param int $substep
2232
 */
2233
function textfield_alter($change, $substep)
2234
{
2235
	global $db_prefix, $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2236
2237
	$request = $smcFunc['db_query']('', '
2238
		SHOW FULL COLUMNS
2239
		FROM {db_prefix}' . $change['table'] . '
2240
		LIKE {string:column}',
2241
		array(
2242
			'column' => $change['column'],
2243
			'db_error_skip' => true,
2244
		)
2245
	);
2246
	if ($smcFunc['db_num_rows']($request) === 0)
2247
		die('Unable to find column ' . $change['column'] . ' inside table ' . $db_prefix . $change['table']);
2248
	$table_row = $smcFunc['db_fetch_assoc']($request);
2249
	$smcFunc['db_free_result']($request);
2250
2251
	// If something of the current column definition is different, fix it.
2252
	$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']);
2253
2254
	// Columns that previously allowed null, need to be converted first.
2255
	$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
2256
2257
	// Get the character set that goes with the collation of the column.
2258 View Code Duplication
	if ($column_fix && !empty($table_row['Collation']))
2259
	{
2260
		$request = $smcFunc['db_query']('', '
2261
			SHOW COLLATION
2262
			LIKE {string:collation}',
2263
			array(
2264
				'collation' => $table_row['Collation'],
2265
				'db_error_skip' => true,
2266
			)
2267
		);
2268
		// No results? Just forget it all together.
2269
		if ($smcFunc['db_num_rows']($request) === 0)
2270
			unset($table_row['Collation']);
2271
		else
2272
			$collation_info = $smcFunc['db_fetch_assoc']($request);
2273
		$smcFunc['db_free_result']($request);
2274
	}
2275
2276
	if ($column_fix)
2277
	{
2278
		// Make sure there are no NULL's left.
2279
		if ($null_fix)
2280
			$smcFunc['db_query']('', '
2281
				UPDATE {db_prefix}' . $change['table'] . '
2282
				SET ' . $change['column'] . ' = {string:default}
2283
				WHERE ' . $change['column'] . ' IS NULL',
2284
				array(
2285
					'default' => isset($change['default']) ? $change['default'] : '',
2286
					'db_error_skip' => true,
2287
				)
2288
			);
2289
2290
		// Do the actual alteration.
2291
		$smcFunc['db_query']('', '
2292
			ALTER TABLE {db_prefix}' . $change['table'] . '
2293
			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}' : ''),
2294
			array(
2295
				'default' => isset($change['default']) ? $change['default'] : '',
2296
				'db_error_skip' => true,
2297
			)
2298
		);
2299
	}
2300
	nextSubstep($substep);
2301
}
2302
2303
// Check if we need to alter this query.
2304
function checkChange(&$change)
2305
{
2306
	global $smcFunc, $db_type, $databases;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2307
	static $database_version, $where_field_support;
2308
2309
	// Attempt to find a database_version.
2310
	if (empty($database_version))
2311
	{
2312
		$database_version = $databases[$db_type]['version_check'];
2313
		$where_field_support = $db_type == 'mysql' && version_compare('5.0', $database_version, '<=');
2314
	}
2315
2316
	// Not a column we need to check on?
2317
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
2318
		return;
2319
2320
	// Break it up you (six|seven).
2321
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
2322
2323
	// Can we support a shortcut method?
2324
	if ($where_field_support)
2325
	{
2326
		// Get the details about this change.
2327
		$request = $smcFunc['db_query']('', '
2328
			SHOW FIELDS
2329
			FROM {db_prefix}{raw:table}
2330
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
2331
			array(
2332
				'table' => $change['table'],
2333
				'old_name' => $temp[1],
2334
				'new_name' => $temp[2],
2335
		));
2336
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2337
		if ($smcFunc['db_num_rows'] != 1)
2338
			return;
2339
2340
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
2341
		$smcFunc['db_free_result']($request);
2342
	}
2343
	else
2344
	{
2345
		// Do this the old fashion, sure method way.
2346
		$request = $smcFunc['db_query']('', '
2347
			SHOW FIELDS
2348
			FROM {db_prefix}{raw:table}',
2349
			array(
2350
				'table' => $change['table'],
2351
		));
2352
		// Mayday!
2353
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2354
		if ($smcFunc['db_num_rows'] == 0)
2355
			return;
2356
2357
		// Oh where, oh where has my little field gone. Oh where can it be...
2358
		while ($row = $smcFunc['db_query']($request))
2359
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
2360
			{
2361
				$current_type = $row['Type'];
2362
				break;
2363
			}
2364
	}
2365
2366
	// If this doesn't match, the column may of been altered for a reason.
2367
	if (trim($current_type) != trim($temp[3]))
2368
		$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...
2369
2370
	// Piece this back together.
2371
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
2372
}
2373
2374
// The next substep.
2375
function nextSubstep($substep)
2376
{
2377
	global $start_time, $timeLimitThreshold, $command_line, $custom_warning;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2378
	global $step_progress, $is_debug, $upcontext;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2379
2380
	if ($_GET['substep'] < $substep)
2381
		$_GET['substep'] = $substep;
2382
2383
	if ($command_line)
2384
	{
2385
		if (time() - $start_time > 1 && empty($is_debug))
2386
		{
2387
			echo '.';
2388
			$start_time = time();
2389
		}
2390
		return;
2391
	}
2392
2393
	@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...
2394
	if (function_exists('apache_reset_timeout'))
2395
		@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...
2396
2397
	if (time() - $start_time <= $timeLimitThreshold)
2398
		return;
2399
2400
	// Do we have some custom step progress stuff?
2401
	if (!empty($step_progress))
2402
	{
2403
		$upcontext['substep_progress'] = 0;
2404
		$upcontext['substep_progress_name'] = $step_progress['name'];
2405
		if ($step_progress['current'] > $step_progress['total'])
2406
			$upcontext['substep_progress'] = 99.9;
2407
		else
2408
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2409
2410
		// Make it nicely rounded.
2411
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2412
	}
2413
2414
	// If this is XML we just exit right away!
2415
	if (isset($_GET['xml']))
2416
		return upgradeExit();
2417
2418
	// We're going to pause after this!
2419
	$upcontext['pause'] = true;
2420
2421
	$upcontext['query_string'] = '';
2422
	foreach ($_GET as $k => $v)
2423
	{
2424
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2425
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2426
	}
2427
2428
	// Custom warning?
2429
	if (!empty($custom_warning))
2430
		$upcontext['custom_warning'] = $custom_warning;
2431
2432
	upgradeExit();
2433
}
2434
2435
function cmdStep0()
2436
{
2437
	global $boarddir, $sourcedir, $modSettings, $start_time, $cachedir, $databases, $db_type, $smcFunc, $upcontext;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2438
	global $is_debug;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2439
	$start_time = time();
2440
2441
	ob_end_clean();
2442
	ob_implicit_flush(true);
2443
	@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...
2444
2445
	if (!isset($_SERVER['argv']))
2446
		$_SERVER['argv'] = array();
2447
	$_GET['maint'] = 1;
2448
2449
	foreach ($_SERVER['argv'] as $i => $arg)
2450
	{
2451
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2452
			$_GET['lang'] = $match[1];
2453
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2454
			continue;
2455
		elseif ($arg == '--no-maintenance')
2456
			$_GET['maint'] = 0;
2457
		elseif ($arg == '--debug')
2458
			$is_debug = true;
2459
		elseif ($arg == '--backup')
2460
			$_POST['backup'] = 1;
2461
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2462
			$_GET['conv'] = 1;
2463
		elseif ($i != 0)
2464
		{
2465
			echo 'SMF Command-line Upgrader
2466
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2467
2468
    --language=LANG         Reset the forum\'s language to LANG.
2469
    --no-maintenance        Don\'t put the forum into maintenance mode.
2470
    --debug                 Output debugging information.
2471
    --backup                Create backups of tables with "backup_" prefix.';
2472
			echo "\n";
2473
			exit;
2474
		}
2475
	}
2476
2477
	if (!php_version_check())
2478
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
2479
	if (!db_version_check())
2480
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2481
2482
	// Do some checks to make sure they have proper privileges
2483
	db_extend('packages');
2484
2485
	// CREATE
2486
	$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');
2487
2488
	// ALTER
2489
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
2490
2491
	// DROP
2492
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2493
2494
	// Sorry... we need CREATE, ALTER and DROP
2495 View Code Duplication
	if (!$create || !$alter || !$drop)
2496
		print_error("The " . $databases[$db_type]['name'] . " user you have set in Settings.php does not have proper privileges.\n\nPlease ask your host to give this user the ALTER, CREATE, and DROP privileges.", true);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal The does not require double quotes, as per coding-style, please use single quotes.

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

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

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

<?php

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

print $doubleQuoted;

will print an indented: Single is Value

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

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

Loading history...
2497
2498
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2499
		&& @file_exists($sourcedir . '/QueryString.php')
2500
		&& @file_exists($sourcedir . '/ManageBoards.php');
2501
	if (!$check && !isset($modSettings['smfVersion']))
2502
		print_error('Error: Some files are missing or out-of-date.', true);
2503
2504
	// Do a quick version spot check.
2505
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
2506
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2507
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2508
		print_error('Error: Some files have not yet been updated properly.');
2509
2510
	// Make sure Settings.php is writable.
2511
	quickFileWritable($boarddir . '/Settings.php');
2512
	if (!is_writable($boarddir . '/Settings.php'))
2513
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2514
2515
	// Make sure Settings_bak.php is writable.
2516
	quickFileWritable($boarddir . '/Settings_bak.php');
2517
	if (!is_writable($boarddir . '/Settings_bak.php'))
2518
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2519
2520 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
2521
		print_error('Error: Unable to obtain write access to "agreement.txt".');
2522
	elseif (isset($modSettings['agreement']))
2523
	{
2524
		$fp = fopen($boarddir . '/agreement.txt', 'w');
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $fp. Configured minimum length is 3.

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

Loading history...
2525
		fwrite($fp, $modSettings['agreement']);
2526
		fclose($fp);
2527
	}
2528
2529
	// Make sure Themes is writable.
2530
	quickFileWritable($modSettings['theme_dir']);
2531
2532
	if (!is_writable($modSettings['theme_dir']) && !isset($modSettings['smfVersion']))
2533
		print_error('Error: Unable to obtain write access to "Themes".');
2534
2535
	// Make sure cache directory exists and is writable!
2536
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
2537
	if (!file_exists($cachedir_temp))
2538
		@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...
2539
2540
	// Make sure the cache temp dir is writable.
2541
	quickFileWritable($cachedir_temp);
2542
2543
	if (!is_writable($cachedir_temp))
2544
		print_error('Error: Unable to obtain write access to "cache".', true);
2545
2546
	// Make sure db_last_error.php is writable.
2547
	quickFileWritable($cachedir_temp . '/db_last_error.php');
2548
	if (!is_writable($cachedir_temp . '/db_last_error.php'))
2549
		print_error('Error: Unable to obtain write access to "db_last_error.php".');
2550
2551
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
2552
		print_error('Error: Unable to find language files!', true);
2553
	else
2554
	{
2555
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
2556
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
2557
2558
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
2559
			print_error('Error: Language files out of date.', true);
2560
		if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
2561
			print_error('Error: Install language is missing for selected language.', true);
2562
2563
		// Otherwise include it!
2564
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
2565
	}
2566
2567
	// Make sure we skip the HTML for login.
2568
	$_POST['upcont'] = true;
2569
	$upcontext['current_step'] = 1;
2570
}
2571
2572
/**
2573
 * Handles converting your database to UTF-8
2574
 */
2575
function ConvertUtf8()
2576
{
2577
	global $upcontext, $db_character_set, $sourcedir, $smcFunc, $modSettings, $language;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2578
	global $db_prefix, $db_type, $command_line, $support_js, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

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

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
3088
3089
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'serialize_json_xml' : 'serialize_json';
3090
	// First thing's first - did we already do this?
3091
	if (!empty($modSettings['json_done']))
3092
	{
3093
		if ($command_line)
3094
			return DeleteUpgrade();
3095
		else
3096
			return true;
3097
	}
3098
3099
	// Done it already - js wise?
3100
	if (!empty($_POST['json_done']))
3101
		return true;
3102
3103
	// List of tables affected by this function
3104
	// name => array('key', col1[,col2|true[,col3]])
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3105
	// If 3rd item in array is true, it indicates that col1 could be empty...
3106
	$tables = array(
3107
		'background_tasks' => array('id_task', 'task_data'),
3108
		'log_actions' => array('id_action', 'extra'),
3109
		'log_online' => array('session', 'url'),
3110
		'log_packages' => array('id_install', 'db_changes', 'failed_steps', 'credits'),
3111
		'log_spider_hits' => array('id_hit', 'url'),
3112
		'log_subscribed' => array('id_sublog', 'pending_details'),
3113
		'pm_rules' => array('id_rule', 'criteria', 'actions'),
3114
		'qanda' => array('id_question', 'answers'),
3115
		'subscriptions' => array('id_subscribe', 'cost'),
3116
		'user_alerts' => array('id_alert', 'extra', true),
3117
		'user_drafts' => array('id_draft', 'to_list', true),
3118
		// These last two are a bit different - we'll handle those separately
3119
		'settings' => array(),
3120
		'themes' => array()
3121
	);
3122
3123
	// Set up some context stuff...
3124
	// Because we're not using numeric indices, we need this to figure out the current table name...
3125
	$keys = array_keys($tables);
3126
3127
	$upcontext['page_title'] = $txt['converting_json'];
3128
	$upcontext['table_count'] = count($keys);
3129
	$upcontext['cur_table_num'] = $_GET['substep'];
3130
	$upcontext['cur_table_name'] = isset($keys[$_GET['substep']]) ? $keys[$_GET['substep']] : $keys[0];
3131
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3132
3133 View Code Duplication
	foreach ($keys as $id => $table)
3134
		if ($id < $_GET['substep'])
3135
			$upcontext['previous_tables'][] = $table;
3136
3137
	if ($command_line)
3138
		echo 'Converting data from serialize() to json_encode().';
3139
3140
	if (!$support_js || isset($_GET['xml']))
3141
	{
3142
		// Fix the data in each table
3143
		for ($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
3144
		{
3145
			$upcontext['cur_table_name'] = isset($keys[$substep + 1]) ? $keys[$substep + 1] : $keys[$substep];
3146
			$upcontext['cur_table_num'] = $substep + 1;
3147
3148
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3149
3150
			// Do we need to pause?
3151
			nextSubstep($substep);
3152
3153
			// Initialize a few things...
3154
			$where = '';
3155
			$vars = array();
3156
			$table = $keys[$substep];
3157
			$info = $tables[$table];
3158
3159
			// Now the fun - build our queries and all that fun stuff
3160
			if ($table == 'settings')
3161
			{
3162
				// Now a few settings...
3163
				$serialized_settings = array(
3164
					'attachment_basedirectories',
3165
					'attachmentUploadDir',
3166
					'cal_today_birthday',
3167
					'cal_today_event',
3168
					'cal_today_holiday',
3169
					'displayFields',
3170
					'last_attachments_directory',
3171
					'memberlist_cache',
3172
					'search_custom_index_config',
3173
					'spider_name_cache'
3174
				);
3175
3176
				// Loop through and fix these...
3177
				$new_settings = array();
3178
				if ($command_line)
3179
					echo "\n" . 'Fixing some settings...';
3180
3181
				foreach ($serialized_settings as $var)
3182
				{
3183
					if (isset($modSettings[$var]))
3184
					{
3185
						// Attempt to unserialize the setting
3186
						$temp = @safe_unserialize($modSettings[$var]);
3187
						if (!$temp && $command_line)
3188
							echo "\n - Failed to unserialize the '" . $var . "' setting. Skipping.";
3189
						elseif ($temp !== false)
3190
							$new_settings[$var] = json_encode($temp);
3191
					}
3192
				}
3193
3194
				// Update everything at once
3195
				if (!function_exists('cache_put_data'))
3196
					require_once($sourcedir . '/Load.php');
3197
				updateSettings($new_settings, true);
3198
3199
				if ($command_line)
3200
					echo ' done.';
3201
			}
3202
			elseif ($table == 'themes')
3203
			{
3204
				// Finally, fix the admin prefs. Unfortunately this is stored per theme, but hopefully they only have one theme installed at this point...
3205
				$query = $smcFunc['db_query']('', '
3206
					SELECT id_member, id_theme, value FROM {db_prefix}themes
3207
					WHERE variable = {string:admin_prefs}',
3208
						array(
3209
							'admin_prefs' => 'admin_preferences'
3210
						)
3211
				);
3212
3213
				if ($smcFunc['db_num_rows']($query) != 0)
3214
				{
3215
					while ($row = $smcFunc['db_fetch_assoc']($query))
3216
					{
3217
						$temp = @safe_unserialize($row['value']);
3218
3219
						if ($command_line)
3220
						{
3221
							if ($temp === false)
3222
								echo "\n" . 'Unserialize of admin_preferences for user ' . $row['id_member'] . ' failed. Skipping.';
3223
							else
3224
								echo "\n" . 'Fixing admin preferences...';
3225
						}
3226
3227
						if ($temp !== false)
3228
						{
3229
							$row['value'] = json_encode($temp);
3230
3231
							// Even though we have all values from the table, UPDATE is still faster than REPLACE
3232
							$smcFunc['db_query']('', '
3233
								UPDATE {db_prefix}themes
3234
								SET value = {string:prefs}
3235
								WHERE id_theme = {int:theme}
3236
									AND id_member = {int:member}
3237
									AND variable = {string:admin_prefs}',
3238
								array(
3239
									'prefs' => $row['value'],
3240
									'theme' => $row['id_theme'],
3241
									'member' => $row['id_member'],
3242
									'admin_prefs' => 'admin_preferences'
3243
								)
3244
							);
3245
3246
							if ($command_line)
3247
								echo ' done.';
3248
						}
3249
					}
3250
3251
					$smcFunc['db_free_result']($query);
3252
				}
3253
			}
3254
			else
3255
			{
3256
				// First item is always the key...
3257
				$key = $info[0];
3258
				unset($info[0]);
3259
3260
				// Now we know what columns we have and such...
3261
				if (count($info) == 2 && $info[2] === true)
3262
				{
3263
					$col_select = $info[1];
3264
					$where = ' WHERE ' . $info[1] . ' != {empty}';
3265
				}
3266
				else
3267
				{
3268
					$col_select = implode(', ', $info);
3269
				}
3270
3271
				$query = $smcFunc['db_query']('', '
3272
					SELECT ' . $key . ', ' . $col_select . '
3273
					FROM {db_prefix}' . $table . $where,
3274
					array()
3275
				);
3276
3277
				if ($smcFunc['db_num_rows']($query) != 0)
3278
				{
3279
					if ($command_line)
3280
					{
3281
						echo "\n" . ' +++ Fixing the "' . $table . '" table...';
3282
						flush();
3283
					}
3284
3285
					while ($row = $smcFunc['db_fetch_assoc']($query))
3286
					{
3287
						$update = '';
3288
3289
						// We already know what our key is...
3290
						foreach ($info as $col)
3291
						{
3292
							if ($col !== true && $row[$col] != '')
3293
							{
3294
								$temp = @safe_unserialize($row[$col]);
3295
3296
								if ($temp === false && $command_line)
3297
								{
3298
									echo "\nFailed to unserialize " . $row[$col] . "... Skipping\n";
3299
								}
3300
								else
3301
								{
3302
									$row[$col] = json_encode($temp);
3303
3304
									// Build our SET string and variables array
3305
									$update .= (empty($update) ? '' : ', ') . $col . ' = {string:' . $col . '}';
3306
									$vars[$col] = $row[$col];
3307
								}
3308
							}
3309
						}
3310
3311
						$vars[$key] = $row[$key];
3312
3313
						// In a few cases, we might have empty data, so don't try to update in those situations...
3314
						if (!empty($update))
3315
						{
3316
							$smcFunc['db_query']('', '
3317
								UPDATE {db_prefix}' . $table . '
3318
								SET ' . $update . '
3319
								WHERE ' . $key . ' = {' . ($key == 'session' ? 'string' : 'int') . ':' . $key . '}',
3320
								$vars
3321
							);
3322
						}
3323
					}
3324
3325
					if ($command_line)
3326
						echo ' done.';
3327
3328
					// Free up some memory...
3329
					$smcFunc['db_free_result']($query);
3330
				}
3331
			}
3332
			// If this is XML to keep it nice for the user do one table at a time anyway!
3333
			if (isset($_GET['xml']))
3334
				return upgradeExit();
3335
		}
3336
3337
		if ($command_line)
3338
		{
3339
			echo "\n" . 'Successful.' . "\n";
3340
			flush();
3341
		}
3342
		$upcontext['step_progress'] = 100;
3343
3344
		// Last but not least, insert a dummy setting so we don't have to do this again in the future...
3345
		updateSettings(array('json_done' => true));
3346
3347
		$_GET['substep'] = 0;
3348
		// Make sure we move on!
3349
		if ($command_line)
3350
			return DeleteUpgrade();
3351
3352
		return true;
3353
	}
3354
3355
	// If this fails we just move on to deleting the upgrade anyway...
3356
	$_GET['substep'] = 0;
3357
	return false;
3358
}
3359
3360
/**
3361
 * As of 2.1, we want to store db_last_error.php in the cache
3362
 * To make that happen, Settings.php needs to ensure the $cachedir path is correct before trying to write to db_last_error.php
3363
 */
3364
function move_db_last_error_to_cachedir()
3365
{
3366
	$settings = file_get_contents(dirname(__FILE__) . '/Settings.php');
3367
3368
	$regex = <<<'EOT'
3369
(\s*#\s*Make\s+sure\s+the\s+paths\s+are\s+correct\.\.\.\s+at\s+least\s+try\s+to\s+fix\s+them\.\s+)?if\s*\(\!file_exists\(\$boarddir\)\s+&&\s+file_exists\(dirname\(__FILE__\)\s+\.\s+'/agreement\.txt'\)\)\s+\$boarddir\s*\=\s*dirname\(__FILE__\);\s+if\s*\(\!file_exists\(\$sourcedir\)\s+&&\s+file_exists\(\$boarddir\s*\.\s*'/Sources'\)\)\s+\$sourcedir\s*\=\s*\$boarddir\s*\.\s*'/Sources';\s+if\s*\(\!file_exists\(\$cachedir\)\s+&&\s+file_exists\(\$boarddir\s*\.\s*'/cache'\)\)\s+\$cachedir\s*\=\s*\$boarddir\s*\.\s*'/cache';
3370
EOT;
3371
3372
	$replacement = <<<'EOT'
3373
# Make sure the paths are correct... at least try to fix them.
3374
if (!file_exists($boarddir) && file_exists(dirname(__FILE__) . '/agreement.txt'))
3375
	$boarddir = dirname(__FILE__);
3376
if (!file_exists($sourcedir) && file_exists($boarddir . '/Sources'))
3377
	$sourcedir = $boarddir . '/Sources';
3378
if (!file_exists($cachedir) && file_exists($boarddir . '/cache'))
3379
	$cachedir = $boarddir . '/cache';
3380
3381
3382
EOT;
3383
3384
	if (preg_match('~' . $regex . '~', $settings) && preg_match('~(#+\s*Error-Catching\s*#+)~', $settings))
3385
	{
3386
		$settings = preg_replace('~' . $regex . '~', '', $settings);
3387
		$settings = preg_replace('~(#+\s*Error-Catching\s*#+)~', $replacement . '$1', $settings);
3388
		$settings = preg_replace('~dirname(__FILE__) . \'/db_last_error.php\'~', '(isset($cachedir) ? $cachedir : dirname(__FILE__)) . \'/db_last_error.php\'', $settings);
3389
3390
		// Blank out the file - done to fix a oddity with some servers.
3391
		file_put_contents(dirname(__FILE__) . '/Settings.php', '');
3392
3393
		file_put_contents(dirname(__FILE__) . '/Settings.php', $settings);
3394
	}
3395
}
3396
3397
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3398
                        Templates are below this point
3399
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
3400
3401
// This is what is displayed if there's any chmod to be done. If not it returns nothing...
3402
function template_chmod()
3403
{
3404
	global $upcontext, $txt, $settings;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
3405
3406
	// Don't call me twice!
3407
	if (!empty($upcontext['chmod_called']))
3408
		return;
3409
3410
	$upcontext['chmod_called'] = true;
3411
3412
	// Nothing?
3413
	if (empty($upcontext['chmod']['files']) && empty($upcontext['chmod']['ftp_error']))
3414
		return;
3415
3416
	// Was it a problem with Windows?
3417
	if (!empty($upcontext['chmod']['ftp_error']) && $upcontext['chmod']['ftp_error'] == 'total_mess')
3418
	{
3419
		echo '
3420
		<div class="error">
3421
			<p>', $txt['upgrade_writable_files'] ,'</p>
3422
			<ul class="error_content">
3423
				<li>' . implode('</li>
3424
				<li>', $upcontext['chmod']['files']) . '</li>
3425
			</ul>
3426
		</div>';
3427
3428
		return false;
3429
	}
3430
3431
	echo '
3432
		<div class="panel">
3433
			<h2>', $txt['upgrade_ftp_login'], '</h2>
3434
			<h3>', $txt['upgrade_ftp_perms'], '</h3>
3435
			<script>
3436
				function warning_popup()
3437
				{
3438
					popup = window.open(\'\',\'popup\',\'height=150,width=400,scrollbars=yes\');
3439
					var content = popup.document;
3440
					content.write(\'<!DOCTYPE html>\n\');
3441
					content.write(\'<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>\n\t<head>\n\t\t<meta name="robots" content="noindex">\n\t\t\');
3442
					content.write(\'<title>', $txt['upgrade_ftp_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\');
3443
					content.write(\'<div class="windowbg description">\n\t\t\t<h4>', $txt['upgrade_ftp_files'], '</h4>\n\t\t\t\');
3444
					content.write(\'<p>', implode('<br>\n\t\t\t', $upcontext['chmod']['files']), '</p>\n\t\t\t\');';
3445
3446
	if (isset($upcontext['systemos']) && $upcontext['systemos'] == 'linux')
3447
		echo '
3448
					content.write(\'<hr>\n\t\t\t\');
3449
					content.write(\'<p>', $txt['upgrade_ftp_shell'], '</p>\n\t\t\t\');
3450
					content.write(\'<tt># chmod a+w ', implode(' ', $upcontext['chmod']['files']), '</tt>\n\t\t\t\');';
3451
3452
	echo '
3453
					content.write(\'<a href="javascript:self.close();">close</a>\n\t\t</div>\n\t</body>\n</html>\');
3454
					content.close();
3455
				}
3456
			</script>';
3457
3458
	if (!empty($upcontext['chmod']['ftp_error']))
3459
		echo '
3460
			<div class="error_message red">
3461
				<p>', $txt['upgrade_ftp_error'], '<p>
3462
				<code>', $upcontext['chmod']['ftp_error'], '</code>
3463
			</div>';
3464
3465
	if (empty($upcontext['chmod_in_form']))
3466
		echo '
3467
			<form action="', $upcontext['form_url'], '" method="post">';
3468
3469
	echo '
3470
				<dl class="settings">
3471
					<dt>
3472
						<label for="ftp_server">', $txt['ftp_server'], ':</label>
3473
					</dt>
3474
					<dd>
3475
						<div class="floatright">
3476
							<label for="ftp_port" class="textbox"><strong>', $txt['ftp_port'], ':</strong></label>
3477
							<input type="text" size="3" name="ftp_port" id="ftp_port" value="', isset($upcontext['chmod']['port']) ? $upcontext['chmod']['port'] : '21', '">
3478
						</div>
3479
						<input type="text" size="30" name="ftp_server" id="ftp_server" value="', isset($upcontext['chmod']['server']) ? $upcontext['chmod']['server'] : 'localhost', '">
3480
						<div class="smalltext">', $txt['ftp_server_info'], '</div>
3481
					</dd>
3482
					<dt>
3483
						<label for="ftp_username">', $txt['ftp_username'], ':</label>
3484
					</dt>
3485
					<dd>
3486
						<input type="text" size="30" name="ftp_username" id="ftp_username" value="', isset($upcontext['chmod']['username']) ? $upcontext['chmod']['username'] : '', '">
3487
						<div class="smalltext">', $txt['ftp_username_info'], '</div>
3488
					</dd>
3489
					<dt>
3490
						<label for="ftp_password">', $txt['ftp_password'], ':</label>
3491
					</dt>
3492
					<dd>
3493
						<input type="password" size="30" name="ftp_password" id="ftp_password">
3494
						<div class="smalltext">', $txt['ftp_password_info'], '</div>
3495
					</dd>
3496
					<dt>
3497
						<label for="ftp_path">', $txt['ftp_path'], ':</label>
3498
					</dt>
3499
					<dd>
3500
						<input type="text" size="30" name="ftp_path" id="ftp_path" value="', isset($upcontext['chmod']['path']) ? $upcontext['chmod']['path'] : '', '">
3501
						<div class="smalltext">', !empty($upcontext['chmod']['path']) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'], '</div>
3502
					</dd>
3503
				</dl>
3504
3505
				<div class="righttext buttons">
3506
					<input type="submit" value="', $txt['ftp_connect'], '" class="button">
3507
				</div>';
3508
3509
	if (empty($upcontext['chmod_in_form']))
3510
		echo '
3511
			</form>';
3512
3513
	echo '
3514
		</div><!-- .panel -->';
3515
}
3516
3517
function template_upgrade_above()
3518
{
3519
	global $modSettings, $txt, $settings, $upcontext, $upgradeurl;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
3520
3521
	echo '<!DOCTYPE html>
3522
<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>
3523
<head>
3524
	<meta charset="', isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8', '">
3525
	<meta name="robots" content="noindex">
3526
	<title>', $txt['upgrade_upgrade_utility'], '</title>
3527
	<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/index.css?alp21">
3528
	<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/install.css?alp21">
3529
	', $txt['lang_rtl'] == true ? '<link rel="stylesheet" href="' . $settings['default_theme_url'] . '/css/rtl.css?alp21">' : '', '
3530
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
3531
	<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
3532
	<script>
3533
		var smf_scripturl = \'', $upgradeurl, '\';
3534
		var smf_charset = \'', (empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'UTF-8' : $txt['lang_character_set']) : $modSettings['global_character_set']), '\';
3535
		var startPercent = ', $upcontext['overall_percent'], ';
3536
3537
		// This function dynamically updates the step progress bar - and overall one as required.
3538
		function updateStepProgress(current, max, overall_weight)
3539
		{
3540
			// What out the actual percent.
3541
			var width = parseInt((current / max) * 100);
3542
			if (document.getElementById(\'step_progress\'))
3543
			{
3544
				document.getElementById(\'step_progress\').style.width = width + "%";
3545
				setInnerHTML(document.getElementById(\'step_text\'), width + "%");
3546
			}
3547
			if (overall_weight && document.getElementById(\'overall_progress\'))
3548
			{
3549
				overall_width = parseInt(startPercent + width * (overall_weight / 100));
3550
				document.getElementById(\'overall_progress\').style.width = overall_width + "%";
3551
				setInnerHTML(document.getElementById(\'overall_text\'), overall_width + "%");
3552
			}
3553
		}
3554
	</script>
3555
</head>
3556
<body>
3557
	<div id="footerfix">
3558
	<div id="header">
3559
		<h1 class="forumtitle">', $txt['upgrade_upgrade_utility'], '</h1>
3560
		<img id="smflogo" src="', $settings['default_theme_url'], '/images/smflogo.svg" alt="Simple Machines Forum" title="Simple Machines Forum">
3561
	</div>
3562
	<div id="wrapper">
3563
		<div id="upper_section">
3564
			<div id="inner_section">
3565
				<div id="inner_wrap">
3566
				</div>
3567
			</div>
3568
		</div>
3569
		<div id="content_section">
3570
			<div id="main_content_section">
3571
				<div id="main_steps">
3572
					<h2>', $txt['upgrade_progress'], '</h2>
3573
					<ul>';
3574
3575 View Code Duplication
	foreach ($upcontext['steps'] as $num => $step)
3576
		echo '
3577
						<li class="', $num < $upcontext['current_step'] ? 'stepdone' : ($num == $upcontext['current_step'] ? 'stepcurrent' : 'stepwaiting'), '">', $txt['upgrade_step'], ' ', $step[0], ': ', $step[1], '</li>';
3578
3579
	echo '
3580
					</ul>
3581
				</div><!-- #main_steps -->
3582
				
3583
				<div id="install_progress">
3584
					<div id="progress_bar" class="progress_bar progress_green">
3585
						<h3>', $txt['upgrade_overall_progress'], '</h3>
3586
						<div id="overall_progress" class="bar" style="width: ', $upcontext['overall_percent'], '%;"></div>
3587
						<span id="overall_text">', $upcontext['overall_percent'], '%</span>
3588
					</div>';
3589
3590
	if (isset($upcontext['step_progress']))
3591
		echo '
3592
					<div id="progress_bar_step" class="progress_bar progress_yellow">
3593
						<h3>', $txt['upgrade_step_progress'], '</h3>
3594
						<div id="step_progress" class="bar" style="width: ', $upcontext['step_progress'], '%;"></div>
3595
						<span id="step_text">', $upcontext['step_progress'], '%</span>
3596
					</div>';
3597
3598
	echo '
3599
					<h3>', isset($upcontext['substep_progress_name']) ? trim(strtr($upcontext['substep_progress_name'], array('.' => ''))) : '', '</h3>
3600
					<div id="substep_bar_div" class="progress_bar" style="display: ', isset($upcontext['substep_progress']) ? '' : 'none', ';">
3601
						<div id="substep_progress" class="bar" style="width: ', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%;"></div>
3602
						<span>', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : '', '%</span>
3603
					</div>';
3604
3605
	// How long have we been running this?
3606
	$elapsed = time() - $upcontext['started'];
3607
	$mins = (int) ($elapsed / 60);
3608
	$seconds = $elapsed - $mins * 60;
3609
	echo '
3610
					<div class="smalltext time_elapsed">
3611
						', $txt['upgrade_time_elapsed'], ':
3612
						<span id="mins_elapsed">', $mins, '</span> ', $txt['upgrade_time_mins'], ', <span id="secs_elapsed">', $seconds, '</span> ', $txt['upgrade_time_secs'], '.
3613
					</div>';
3614
	echo '
3615
				</div><!-- #install_progress -->
3616
			</div><!-- #main_content_section -->
3617
		</div><!-- #content_section -->
3618
		<div id="main_screen" class="clear">
3619
			<h2>', $upcontext['page_title'], '</h2>
3620
			<div class="panel">';
3621
}
3622
3623
function template_upgrade_below()
3624
{
3625
	global $upcontext, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
3626
3627
	if (!empty($upcontext['pause']))
3628
		echo '
3629
					<em>', $txt['upgrade_incomplete'], '.</em><br>
3630
3631
					<h2 style="margin-top: 2ex;">', $txt['upgrade_not_quite_done'], '</h2>
3632
					<h3>
3633
						', $txt['upgrade_paused_overload'], '
3634
					</h3>';
3635
3636
	if (!empty($upcontext['custom_warning']))
3637
		echo '
3638
					<div class="errorbox">
3639
						<h3>', $txt['upgrade_note'], '</h3>
3640
						', $upcontext['custom_warning'], '
3641
					</div>';
3642
3643
	echo '
3644
					<div class="righttext" style="margin: 1ex;">';
3645
3646
	if (!empty($upcontext['continue']))
3647
		echo '
3648
						<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '"', $upcontext['continue'] == 2 ? ' disabled' : '', ' class="button">';
3649
	if (!empty($upcontext['skip']))
3650
		echo '
3651
						<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="dontSubmit = true; document.getElementById(\'contbutt\').disabled = \'disabled\'; return true;" class="button">';
3652
3653
	echo '
3654
					</div>
3655
				</form>
3656
			</div><!-- .panel -->
3657
		</div><!-- #main_screen -->
3658
	</div><!-- #wrapper -->
3659
	</div><!-- #footerfix -->
3660
	<div id="footer">
3661
		<ul>
3662
			<li class="copyright"><a href="https://www.simplemachines.org/" title="Simple Machines Forum" target="_blank" rel="noopener">SMF &copy; 2018, Simple Machines</a></li>
3663
		</ul>
3664
	</div>';
3665
3666
	// Are we on a pause?
3667
	if (!empty($upcontext['pause']))
3668
	{
3669
		echo '
3670
	<script>
3671
		window.onload = doAutoSubmit;
3672
		var countdown = 3;
3673
		var dontSubmit = false;
3674
3675
		function doAutoSubmit()
3676
		{
3677
			if (countdown == 0 && !dontSubmit)
3678
				document.upform.submit();
3679
			else if (countdown == -1)
3680
				return;
3681
3682
			document.getElementById(\'contbutt\').value = "', $txt['upgrade_continue'], ' (" + countdown + ")";
3683
			countdown--;
3684
3685
			setTimeout("doAutoSubmit();", 1000);
3686
		}
3687
	</script>';
3688
	}
3689
3690
	echo '
3691
</body>
3692
</html>';
3693
}
3694
3695
function template_xml_above()
3696
{
3697
	global $upcontext;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
3698
3699
	echo '<', '?xml version="1.0" encoding="UTF-8"?', '>
3700
	<smf>';
3701
3702
	if (!empty($upcontext['get_data']))
3703
		foreach ($upcontext['get_data'] as $k => $v)
3704
			echo '
3705
		<get key="', $k, '">', $v, '</get>';
3706
}
3707
3708
function template_xml_below()
3709
{
3710
	echo '
3711
	</smf>';
3712
}
3713
3714
function template_error_message()
3715
{
3716
	global $upcontext;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
3717
3718
	echo '
3719
	<div class="error_message red">
3720
		', $upcontext['error_msg'], '
3721
		<br>
3722
		<a href="', $_SERVER['PHP_SELF'], '">Click here to try again.</a>
3723
	</div>';
3724
}
3725
3726
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 (L2025-2071) 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...
3727
{
3728
	global $upcontext, $disable_security, $settings, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
3729
3730
	echo '
3731
				<script src="https://www.simplemachines.org/smf/current-version.js?version=' . SMF_VERSION . '"></script>
3732
				
3733
				<h3>', sprintf($txt['upgrade_ready_proceed'], SMF_VERSION), '</h3>
3734
				<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">
3735
					<input type="hidden" name="', $upcontext['login_token_var'], '" value="', $upcontext['login_token'], '">
3736
3737
					<div id="version_warning" class="noticebox" style="display: none;">
3738
						<h3>', $txt['upgrade_warning'], '</h3>
3739
						', sprintf($txt['upgrade_warning_out_of_date'], SMF_VERSION, 'https://www.simplemachines.org'), '
3740
					</div>';
3741
3742
	$upcontext['chmod_in_form'] = true;
3743
	template_chmod();
3744
3745
	// For large, pre 1.1 RC2 forums give them a warning about the possible impact of this upgrade!
3746
	if ($upcontext['is_large_forum'])
3747
		echo '
3748
					<div class="errorbox">
3749
						<h3>', $txt['upgrade_warning'], '</h3>
3750
						', $txt['upgrade_warning_lots_data'], '
3751
					</div>';
3752
3753
	// A warning message?
3754
	if (!empty($upcontext['warning']))
3755
		echo '
3756
					<div class="errorbox">
3757
						<h3>', $txt['upgrade_warning'], '</h3>
3758
						', $upcontext['warning'], '
3759
					</div>';
3760
3761
	// Paths are incorrect?
3762
	echo '
3763
					<div class="errorbox"', (file_exists($settings['default_theme_dir'] . '/scripts/script.js') ? ' style="display: none;"' : ''), ' id="js_script_missing_error">
3764
						<h3>', $txt['upgrade_critical_error'], '</h3>
3765
						', sprintf($txt['upgrade_error_script_js'], 'https://www.simplemachines.org'), '
3766
					</div>';
3767
3768
	// Is there someone already doing this?
3769
	if (!empty($upcontext['user']['id']) && (time() - $upcontext['started'] < 72600 || time() - $upcontext['updated'] < 3600))
3770
	{
3771
		$ago = time() - $upcontext['started'];
3772
		if ($ago < 60)
3773
			$ago = $ago . ' seconds';
3774
		elseif ($ago < 3600)
3775
			$ago = (int) ($ago / 60) . ' minutes';
3776
		else
3777
			$ago = (int) ($ago / 3600) . ' hours';
3778
3779
		$active = time() - $upcontext['updated'];
3780
		if ($active < 60)
3781
			$updated = $active . ' seconds';
3782
		elseif ($active < 3600)
3783
			$updated = (int) ($active / 60) . ' minutes';
3784
		else
3785
			$updated = (int) ($active / 3600) . ' hours';
3786
3787
		echo '
3788
					<div class="errorbox">
3789
						<h3>', $txt['upgrade_warning'], '</h3>
3790
						<p>', sprintf($txt['upgrade_time'], $upcontext['user']['name'], $ago, $updated), '</p>';
3791
		if ($active < 600)
3792
			echo '
3793
						<p>', $txt['upgrade_run_script'], ' ', $upcontext['user']['name'],' ', $txt['upgrade_run_script2'], '</p>';
3794
3795
		if ($active > $upcontext['inactive_timeout'])
3796
			echo '
3797
						<p>',$txt['upgrade_run'], '</p>';
3798
		else
3799
			echo '
3800
						<p>', $txt['upgrade_script_timeout'], ' ', $upcontext['user']['name'], ' ', $txt['upgrade_script_timeout2'], ' ', ($upcontext['inactive_timeout'] > 120 ? round($upcontext['inactive_timeout'] / 60, 1) . ' minutes!' : $upcontext['inactive_timeout'] . ' seconds!'), '</p>';
3801
3802
		echo '
3803
					</div>';
3804
	}
3805
3806
	echo '
3807
					<strong>', $txt['upgrade_admin_login'], ' ', $disable_security ? '(DISABLED)' : '', '</strong>
3808
					<h3>', $txt['upgrade_sec_login'], '</h3>
3809
					<dl class="settings">
3810
						<dt>
3811
							<label for="user"', $disable_security ? ' disabled' : '', '>', $txt['upgrade_username'], '</label>
3812
						</dt>
3813
						<dd>
3814
							<input type="text" name="user" value="', !empty($upcontext['username']) ? $upcontext['username'] : '', '"', $disable_security ? ' disabled' : '', '>';
3815
3816
	if (!empty($upcontext['username_incorrect']))
3817
		echo '
3818
							<div class="smalltext red">', $txt['upgrade_wrong_username'], '</div>';
3819
3820
	echo '
3821
						</dd>
3822
						<dt>
3823
							<label for="passwrd"', $disable_security ? ' disabled' : '', '>', $txt['upgrade_password'], '</label>
3824
						</dt>
3825
						<dd>
3826
							<input type="password" name="passwrd" value=""', $disable_security ? ' disabled' : '', '>
3827
							<input type="hidden" name="hash_passwrd" value="">';
3828
3829
	if (!empty($upcontext['password_failed']))
3830
		echo '
3831
							<div class="smalltext red">', $txt['upgrade_wrong_password'], '</div>';
3832
3833
	echo '
3834
						</dd>';
3835
3836
	// Can they continue?
3837
	if (!empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] >= $upcontext['inactive_timeout'] && $upcontext['user']['step'] > 1)
3838
	{
3839
		echo '
3840
						<dd>
3841
							<label for="cont"><input type="checkbox" id="cont" name="cont" checked>', $txt['upgrade_continue_step'], '</label>
3842
						</dd>';
3843
	}
3844
3845
	echo '
3846
					</dl>
3847
					<span class="smalltext">
3848
						', $txt['upgrade_bypass'], '
3849
					</span>
3850
					<input type="hidden" name="login_attempt" id="login_attempt" value="1">
3851
					<input type="hidden" name="js_works" id="js_works" value="0">';
3852
3853
	// Say we want the continue button!
3854
	$upcontext['continue'] = !empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] < $upcontext['inactive_timeout'] ? 2 : 1;
3855
3856
	// This defines whether javascript is going to work elsewhere :D
3857
	echo '
3858
					<script>
3859
						if (\'XMLHttpRequest\' in window && document.getElementById(\'js_works\'))
3860
							document.getElementById(\'js_works\').value = 1;
3861
3862
						// Latest version?
3863
						function smfCurrentVersion()
3864
						{
3865
							var smfVer, yourVer;
3866
3867
							if (!(\'smfVersion\' in window))
3868
								return;
3869
3870
							window.smfVersion = window.smfVersion.replace(/SMF\s?/g, \'\');
3871
3872
							smfVer = document.getElementById(\'smfVersion\');
3873
							yourVer = document.getElementById(\'yourVersion\');
3874
3875
							setInnerHTML(smfVer, window.smfVersion);
3876
3877
							var currentVersion = getInnerHTML(yourVer);
3878
							if (currentVersion < window.smfVersion)
3879
								document.getElementById(\'version_warning\').style.display = \'\';
3880
						}
3881
						addLoadEvent(smfCurrentVersion);
3882
3883
						// This checks that the script file even exists!
3884
						if (typeof(smfSelectText) == \'undefined\')
3885
							document.getElementById(\'js_script_missing_error\').style.display = \'\';
3886
3887
					</script>';
3888
}
3889
3890
function template_upgrade_options()
3891
{
3892
	global $upcontext, $modSettings, $db_prefix, $mmessage, $mtitle, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
3893
3894
	echo '
3895
				<h3>', $txt['upgrade_areyouready'], '</h3>
3896
				<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">';
3897
3898
	// Warning message?
3899
	if (!empty($upcontext['upgrade_options_warning']))
3900
		echo '
3901
				<div class="errorbox">
3902
					<h3>', $txt['upgrade_warning'] ,'</h3>
3903
					', $upcontext['upgrade_options_warning'], '
3904
				</div>';
3905
3906
	echo '
3907
				<ul class="upgrade_settings">
3908
					<li>
3909
						<input type="checkbox" name="backup" id="backup" value="1">
3910
						<label for="backup">', $txt['upgrade_backup_table'], ' &quot;backup_' . $db_prefix . '&quot;.</label>
3911
						(', $txt['upgrade_recommended'], ')
3912
					</li>
3913
					<li>
3914
						<input type="checkbox" name="maint" id="maint" value="1" checked>
3915
						<label for="maint">', $txt['upgrade_maintenace'], '</label>
3916
						<span class="smalltext">(<a href="#" onclick="document.getElementById(\'mainmess\').style.display = document.getElementById(\'mainmess\').style.display == \'\' ? \'none\' : \'\'">', $txt['upgrade_customize'], '</a>)</span>
3917
						<div id="mainmess" style="display: none;">
3918
							<strong class="smalltext">', $txt['upgrade_maintenance_title'], ' </strong><br>
3919
							<input type="text" name="maintitle" size="30" value="', htmlspecialchars($mtitle), '"><br>
3920
							<strong class="smalltext">', $txt['upgrade_maintenace_message'], ' </strong><br>
3921
							<textarea name="mainmessage" rows="3" cols="50">', htmlspecialchars($mmessage), '</textarea>
3922
						</div>
3923
					</li>
3924
					<li>
3925
						<input type="checkbox" name="debug" id="debug" value="1">
3926
						<label for="debug">'.$txt['upgrade_debug_info'], '</label>
3927
					</li>
3928
					<li>
3929
						<input type="checkbox" name="empty_error" id="empty_error" value="1">
3930
						<label for="empty_error">', $txt['upgrade_empty_errlog'], '</label>
3931
					</li>';
3932
3933
	if (!empty($upcontext['karma_installed']['good']) || !empty($upcontext['karma_installed']['bad']))
3934
		echo '
3935
					<li>
3936
						<input type="checkbox" name="delete_karma" id="delete_karma" value="1">
3937
						<label for="delete_karma">', $txt['upgrade_delete_karma'], '</label>
3938
					</li>';
3939
3940
	echo '
3941
					<li>
3942
						<input type="checkbox" name="stats" id="stats" value="1"', empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']) ? '' : ' checked="checked"', '>
3943
						<label for="stat">
3944
							', $txt['upgrade_stats_collection'], '<br>
3945
							<span class="smalltext">', sprintf($txt['upgrade_stats_info'], 'https://www.simplemachines.org/about/stats.php'), '</a></span>
3946
						</label>
3947
					</li>
3948
				</ul>
3949
				<input type="hidden" name="upcont" value="1">';
3950
3951
	// We need a normal continue button here!
3952
	$upcontext['continue'] = 1;
3953
}
3954
3955
// Template for the database backup tool/
3956
function template_backup_database()
3957
{
3958
	global $upcontext, $support_js, $is_debug, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
3959
3960
	echo '
3961
				<h3>', $txt['upgrade_wait'], '</h3>';
3962
3963
	echo '
3964
				<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
3965
					<input type="hidden" name="backup_done" id="backup_done" value="0">
3966
					<strong>', sprintf($txt['upgrade_completedtables_outof'], $upcontext['cur_table_num'], $upcontext['table_count']) ,'</strong>
3967
					<div id="debug_section">
3968
						<span id="debuginfo"></span>
3969
					</div>';
3970
3971
	// Dont any tables so far?
3972 View Code Duplication
	if (!empty($upcontext['previous_tables']))
3973
		foreach ($upcontext['previous_tables'] as $table)
3974
			echo '
3975
					<br>', $txt['upgrade_completed_table'], ' &quot;', $table, '&quot;.';
3976
3977
	echo '
3978
					<h3 id="current_tab">
3979
						', $txt['upgrade_current_table'], ' &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;
3980
					</h3>
3981
					<p id="commess" style="display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">Backup Complete! Click Continue to Proceed.</p>';
3982
3983
	// Continue please!
3984
	$upcontext['continue'] = $support_js ? 2 : 1;
3985
3986
	// If javascript allows we want to do this using XML.
3987 View Code Duplication
	if ($support_js)
3988
	{
3989
		echo '
3990
					<script>
3991
						var lastTable = ', $upcontext['cur_table_num'], ';
3992
						function getNextTables()
3993
						{
3994
							getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
3995
						}
3996
3997
						// Got an update!
3998
						function onBackupUpdate(oXMLDoc)
3999
						{
4000
							var sCurrentTableName = "";
4001
							var iTableNum = 0;
4002
							var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4003
							for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4004
								sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4005
							iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4006
4007
							// Update the page.
4008
							setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4009
							setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4010
							lastTable = iTableNum;
4011
							updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4012
4013
		// If debug flood the screen.
4014
		if ($is_debug)
4015
			echo '
4016
							setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4017
4018
							if (document.getElementById(\'debug_section\').scrollHeight)
4019
								document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4020
4021
		echo '
4022
							// Get the next update...
4023
							if (iTableNum == ', $upcontext['table_count'], ')
4024
							{
4025
								document.getElementById(\'commess\').style.display = "";
4026
								document.getElementById(\'current_tab\').style.display = "none";
4027
								document.getElementById(\'contbutt\').disabled = 0;
4028
								document.getElementById(\'backup_done\').value = 1;
4029
							}
4030
							else
4031
								getNextTables();
4032
						}
4033
						getNextTables();
4034
					//# sourceURL=dynamicScript-bkup.js
4035
					</script>';
4036
	}
4037
}
4038
4039
function template_backup_xml()
4040
{
4041
	global $upcontext;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
4042
4043
	echo '
4044
		<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4045
}
4046
4047
// Here is the actual "make the changes" template!
4048
function template_database_changes()
4049
{
4050
	global $upcontext, $support_js, $is_debug, $timeLimitThreshold, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
4051
4052
	if (empty($is_debug) && !empty($upcontext['upgrade_status']['debug']))
4053
		$is_debug = true;
4054
4055
	echo '
4056
				<h3>', $txt['upgrade_db_changes'], '</h3>
4057
				<h4><em>', $txt['upgrade_db_patient'], '</em></h4>';
4058
4059
	echo '
4060
				<form action="', $upcontext['form_url'], '&amp;filecount=', $upcontext['file_count'], '" name="upform" id="upform" method="post">
4061
					<input type="hidden" name="database_done" id="database_done" value="0">';
4062
4063
	// No javascript looks rubbish!
4064
	if (!$support_js)
4065
	{
4066
		foreach ($upcontext['actioned_items'] as $num => $item)
4067
		{
4068
			if ($num != 0)
4069
				echo ' Successful!';
4070
			echo '<br>' . $item;
4071
		}
4072
		if (!empty($upcontext['changes_complete']))
4073
		{
4074
			if ($is_debug)
4075
			{
4076
				$active = time() - $upcontext['started'];
4077
				$hours = floor($active / 3600);
4078
				$minutes = intval(($active / 60) % 60);
4079
				$seconds = intval($active % 60);
4080
4081
				$totalTime = '';
4082 View Code Duplication
				if ($hours > 0)
4083
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
4084 View Code Duplication
				if ($minutes > 0)
4085
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
4086 View Code Duplication
				if ($seconds > 0)
4087
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
4088
			}
4089
4090
			if ($is_debug && !empty($totalTime))
4091
				echo '', sprintf($txt['upgrade_success_time'], $totalTime), '<br>';
4092
			else
4093
				echo '', $txt['upgrade_success'], '<br>';
4094
4095
			echo '
4096
					<p id="commess">', $txt['upgrade_db_complete'], '</p>';
4097
		}
4098
	}
4099
	else
4100
	{
4101
		// Tell them how many files we have in total.
4102
		if ($upcontext['file_count'] > 1)
4103
			echo '
4104
					<strong id="info1">', $txt['upgrade_script'], ' <span id="file_done">', $upcontext['cur_file_num'], '</span> of ', $upcontext['file_count'], '.</strong>';
4105
4106
		echo '
4107
					<h3 id="info2">
4108
						<strong>', $txt['upgrade_executing'], '</strong> &quot;<span id="cur_item_name">', $upcontext['current_item_name'], '</span>&quot; (<span id="item_num">', $upcontext['current_item_num'], '</span> ', $txt['upgrade_of'], ' <span id="total_items"><span id="item_count">', $upcontext['total_items'], '</span>', $upcontext['file_count'] > 1 ? ' - of this script' : '', ')</span>
4109
					</h3>
4110
					<p id="commess" style="display: ', !empty($upcontext['changes_complete']) || $upcontext['current_debug_item_num'] == $upcontext['debug_items'] ? 'inline' : 'none', ';">', $txt['upgrade_db_complete2'], '</p>';
4111
4112
		if ($is_debug)
4113
		{
4114
			if ($upcontext['current_debug_item_num'] == $upcontext['debug_items'])
4115
			{
4116
				$active = time() - $upcontext['started'];
4117
				$hours = floor($active / 3600);
4118
				$minutes = intval(($active / 60) % 60);
4119
				$seconds = intval($active % 60);
4120
4121
				$totalTime = '';
4122 View Code Duplication
				if ($hours > 0)
4123
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
4124 View Code Duplication
				if ($minutes > 0)
4125
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
4126 View Code Duplication
				if ($seconds > 0)
4127
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
4128
			}
4129
4130
			echo '
4131
					<p id="upgradeCompleted">';
4132
4133
			if (!empty($totalTime))
4134
				echo '', sprintf($txt['upgrade_completed_time2'], $totalTime), '';
4135
4136
			echo '
4137
					</p>
4138
					<div id="debug_section">
4139
						<span id="debuginfo"></span>
4140
					</div>';
4141
		}
4142
	}
4143
4144
	// Place for the XML error message.
4145
	echo '
4146
					<div id="error_block" class="errorbox"', empty($upcontext['error_message']) ? 'style="display: none;"' : '', '>
4147
						<h3>', $txt['upgrade_error'], '</h3>
4148
						<div id="error_message">', isset($upcontext['error_message']) ? $upcontext['error_message'] : $txt['upgrade_unknown_error'], '</div>
4149
					</div>';
4150
4151
	// We want to continue at some point!
4152
	$upcontext['continue'] = $support_js ? 2 : 1;
4153
4154
	// If javascript allows we want to do this using XML.
4155
	if ($support_js)
4156
	{
4157
		echo '
4158
					<script>
4159
						var lastItem = ', $upcontext['current_debug_item_num'], ';
4160
						var sLastString = "', strtr($upcontext['current_debug_item_name'], array('"' => '&quot;')), '";
4161
						var iLastSubStepProgress = -1;
4162
						var curFile = ', $upcontext['cur_file_num'], ';
4163
						var totalItems = 0;
4164
						var prevFile = 0;
4165
						var retryCount = 0;
4166
						var testvar = 0;
4167
						var timeOutID = 0;
4168
						var getData = "";
4169
						var debugItems = ', $upcontext['debug_items'], ';';
4170
4171
		if ($is_debug)
4172
			echo '
4173
						var upgradeStartTime = ' . $upcontext['started'] . ';';
4174
4175
		echo '
4176
						function getNextItem()
4177
						{
4178
							// We want to track this...
4179
							if (timeOutID)
4180
								clearTimeout(timeOutID);
4181
							timeOutID = window.setTimeout("retTimeout()", ', (10 * $timeLimitThreshold), '000);
4182
4183
							getXMLDocument(\'', $upcontext['form_url'], '&xml&filecount=', $upcontext['file_count'], '&substep=\' + lastItem + getData, onItemUpdate);
4184
						}
4185
4186
						// Got an update!
4187
						function onItemUpdate(oXMLDoc)
4188
						{
4189
							var sItemName = "";
4190
							var sDebugName = "";
4191
							var iItemNum = 0;
4192
							var iSubStepProgress = -1;
4193
							var iDebugNum = 0;
4194
							var bIsComplete = 0;
4195
							getData = "";
4196
4197
							// We\'ve got something - so reset the timeout!
4198
							if (timeOutID)
4199
								clearTimeout(timeOutID);
4200
4201
							// Assume no error at this time...
4202
							document.getElementById("error_block").style.display = "none";
4203
4204
							// Are we getting some duff info?
4205
							if (!oXMLDoc.getElementsByTagName("item")[0])
4206
							{
4207
								// Too many errors?
4208
								if (retryCount > 15)
4209
								{
4210
									document.getElementById("error_block").style.display = "";
4211
									setInnerHTML(document.getElementById("error_message"), "Error retrieving information on step: " + (sDebugName == "" ? sLastString : sDebugName));';
4212
4213
	if ($is_debug)
4214
		echo '
4215
									setOuterHTML(document.getElementById(\'debuginfo\'), \'<span class="red">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4216
4217
	echo '
4218
								}
4219
								else
4220
								{
4221
									retryCount++;
4222
									getNextItem();
4223
								}
4224
								return false;
4225
							}
4226
4227
							// Never allow loops.
4228
							if (curFile == prevFile)
4229
							{
4230
								retryCount++;
4231
								if (retryCount > 10)
4232
								{
4233
									document.getElementById("error_block").style.display = "";
4234
									setInnerHTML(document.getElementById("error_message"), "', $txt['upgrade_loop'], '" + sDebugName);';
4235
4236
	if ($is_debug)
4237
		echo '
4238
									setOuterHTML(document.getElementById(\'debuginfo\'), \'<span class="red">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4239
4240
	echo '
4241
								}
4242
							}
4243
							retryCount = 0;
4244
4245
							for (var i = 0; i < oXMLDoc.getElementsByTagName("item")[0].childNodes.length; i++)
4246
								sItemName += oXMLDoc.getElementsByTagName("item")[0].childNodes[i].nodeValue;
4247
							for (var i = 0; i < oXMLDoc.getElementsByTagName("debug")[0].childNodes.length; i++)
4248
								sDebugName += oXMLDoc.getElementsByTagName("debug")[0].childNodes[i].nodeValue;
4249
							for (var i = 0; i < oXMLDoc.getElementsByTagName("get").length; i++)
4250
							{
4251
								getData += "&" + oXMLDoc.getElementsByTagName("get")[i].getAttribute("key") + "=";
4252
								for (var j = 0; j < oXMLDoc.getElementsByTagName("get")[i].childNodes.length; j++)
4253
								{
4254
									getData += oXMLDoc.getElementsByTagName("get")[i].childNodes[j].nodeValue;
4255
								}
4256
							}
4257
4258
							iItemNum = oXMLDoc.getElementsByTagName("item")[0].getAttribute("num");
4259
							iDebugNum = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("num"));
4260
							bIsComplete = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("complete"));
4261
							iSubStepProgress = parseFloat(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("percent"));
4262
							sLastString = sDebugName + " (Item: " + iDebugNum + ")";
4263
4264
							curFile = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("num"));
4265
							debugItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("debug_items"));
4266
							totalItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("items"));
4267
4268
							// If we have an error we haven\'t completed!
4269
							if (oXMLDoc.getElementsByTagName("error")[0] && bIsComplete)
4270
								iDebugNum = lastItem;
4271
4272
							// Do we have the additional progress bar?
4273
							if (iSubStepProgress != -1)
4274
							{
4275
								document.getElementById("substep_bar_div").style.display = "";
4276
								document.getElementById("substep_progress").style.width = iSubStepProgress + "%";
4277
								setInnerHTML(document.getElementById("substep_text"), iSubStepProgress + "%");
4278
								setInnerHTML(document.getElementById("substep_bar_div"), sDebugName.replace(/\./g, "") + ":");
4279
							}
4280
							else
4281
							{
4282
								document.getElementById("substep_bar_div").style.display = "none";
4283
							}
4284
4285
							// Move onto the next item?
4286
							if (bIsComplete)
4287
								lastItem = iDebugNum;
4288
							else
4289
								lastItem = iDebugNum - 1;
4290
4291
							// Are we finished?
4292
							if (bIsComplete && iDebugNum == -1 && curFile >= ', $upcontext['file_count'], ')
4293
							{';
4294
4295
		if ($is_debug)
4296
			echo '
4297
								document.getElementById(\'debug_section\').style.display = "none";
4298
4299
								var upgradeFinishedTime = parseInt(oXMLDoc.getElementsByTagName("curtime")[0].childNodes[0].nodeValue);
4300
								var diffTime = upgradeFinishedTime - upgradeStartTime;
4301
								var diffHours = Math.floor(diffTime / 3600);
4302
								var diffMinutes = parseInt((diffTime / 60) % 60);
4303
								var diffSeconds = parseInt(diffTime % 60);
4304
4305
								var totalTime = "";
4306
								if (diffHours > 0)
4307
									totalTime = totalTime + diffHours + " hour" + (diffHours > 1 ? "s" : "") + " ";
4308
								if (diffMinutes > 0)
4309
									totalTime = totalTime + diffMinutes + " minute" + (diffMinutes > 1 ? "s" : "") + " ";
4310
								if (diffSeconds > 0)
4311
									totalTime = totalTime + diffSeconds + " second" + (diffSeconds > 1 ? "s" : "");
4312
4313
								setInnerHTML(document.getElementById("upgradeCompleted"), "Completed in " + totalTime);';
4314
4315
		echo '
4316
4317
								document.getElementById(\'commess\').style.display = "";
4318
								document.getElementById(\'contbutt\').disabled = 0;
4319
								document.getElementById(\'database_done\').value = 1;';
4320
4321
		if ($upcontext['file_count'] > 1)
4322
			echo '
4323
								document.getElementById(\'info1\').style.display = "none";';
4324
4325
		echo '
4326
								document.getElementById(\'info2\').style.display = "none";
4327
								updateStepProgress(100, 100, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4328
								return true;
4329
							}
4330
							// Was it the last step in the file?
4331
							else if (bIsComplete && iDebugNum == -1)
4332
							{
4333
								lastItem = 0;
4334
								prevFile = curFile;';
4335
4336
		if ($is_debug)
4337
			echo '
4338
								setOuterHTML(document.getElementById(\'debuginfo\'), \'Moving to next script file...done<br><span id="debuginfo"><\' + \'/span>\');';
4339
4340
		echo '
4341
								getNextItem();
4342
								return true;
4343
							}';
4344
4345
		// If debug scroll the screen.
4346
		if ($is_debug)
4347
			echo '
4348
							if (iLastSubStepProgress == -1)
4349
							{
4350
								// Give it consistent dots.
4351
								dots = sDebugName.match(/\./g);
4352
								numDots = dots ? dots.length : 0;
4353
								for (var i = numDots; i < 3; i++)
4354
									sDebugName += ".";
4355
								setOuterHTML(document.getElementById(\'debuginfo\'), sDebugName + \'<span id="debuginfo"><\' + \'/span>\');
4356
							}
4357
							iLastSubStepProgress = iSubStepProgress;
4358
4359
							if (bIsComplete)
4360
								setOuterHTML(document.getElementById(\'debuginfo\'), \'done<br><span id="debuginfo"><\' + \'/span>\');
4361
							else
4362
								setOuterHTML(document.getElementById(\'debuginfo\'), \'...<span id="debuginfo"><\' + \'/span>\');
4363
4364
							if (document.getElementById(\'debug_section\').scrollHeight)
4365
								document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4366
4367
		echo '
4368
							// Update the page.
4369
							setInnerHTML(document.getElementById(\'item_num\'), iItemNum);
4370
							setInnerHTML(document.getElementById(\'cur_item_name\'), sItemName);';
4371
4372
		if ($upcontext['file_count'] > 1)
4373
		{
4374
			echo '
4375
							setInnerHTML(document.getElementById(\'file_done\'), curFile);
4376
							setInnerHTML(document.getElementById(\'item_count\'), totalItems);';
4377
		}
4378
4379
		echo '
4380
							// Is there an error?
4381
							if (oXMLDoc.getElementsByTagName("error")[0])
4382
							{
4383
								var sErrorMsg = "";
4384
								for (var i = 0; i < oXMLDoc.getElementsByTagName("error")[0].childNodes.length; i++)
4385
									sErrorMsg += oXMLDoc.getElementsByTagName("error")[0].childNodes[i].nodeValue;
4386
								document.getElementById("error_block").style.display = "";
4387
								setInnerHTML(document.getElementById("error_message"), sErrorMsg);
4388
								return false;
4389
							}
4390
4391
							// Get the progress bar right.
4392
							barTotal = debugItems * ', $upcontext['file_count'], ';
4393
							barDone = (debugItems * (curFile - 1)) + lastItem;
4394
4395
							updateStepProgress(barDone, barTotal, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4396
4397
							// Finally - update the time here as it shows the server is responding!
4398
							curTime = new Date();
4399
							iElapsed = (curTime.getTime() / 1000 - ', $upcontext['started'], ');
4400
							mins = parseInt(iElapsed / 60);
4401
							secs = parseInt(iElapsed - mins * 60);
4402
							setInnerHTML(document.getElementById("mins_elapsed"), mins);
4403
							setInnerHTML(document.getElementById("secs_elapsed"), secs);
4404
4405
							getNextItem();
4406
							return true;
4407
						}
4408
4409
						// What if we timeout?!
4410
						function retTimeout(attemptAgain)
4411
						{
4412
							// Oh noes...
4413
							if (!attemptAgain)
4414
							{
4415
								document.getElementById("error_block").style.display = "";
4416
								setInnerHTML(document.getElementById("error_message"), "', sprintf($txt['upgrade_repondtime'], ($timeLimitThreshold * 10)), '" + "<a href=\"#\" onclick=\"retTimeout(true); return false;\">', $txt['upgrade_respondtime_clickhere'], '</a>");
4417
							}
4418
							else
4419
							{
4420
								document.getElementById("error_block").style.display = "none";
4421
								getNextItem();
4422
							}
4423
						}';
4424
4425
		// Start things off assuming we've not errored.
4426
		if (empty($upcontext['error_message']))
4427
			echo '
4428
						getNextItem();';
4429
4430
		echo '
4431
					//# sourceURL=dynamicScript-dbch.js
4432
					</script>';
4433
	}
4434
	return;
4435
}
4436
4437
function template_database_xml()
4438
{
4439
	global $is_debug, $upcontext;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
4440
4441
	echo '
4442
	<file num="', $upcontext['cur_file_num'], '" items="', $upcontext['total_items'], '" debug_items="', $upcontext['debug_items'], '">', $upcontext['cur_file_name'], '</file>
4443
	<item num="', $upcontext['current_item_num'], '">', $upcontext['current_item_name'], '</item>
4444
	<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>';
4445
4446
	if (!empty($upcontext['error_message']))
4447
		echo '
4448
	<error>', $upcontext['error_message'], '</error>';
4449
4450
	if (!empty($upcontext['error_string']))
4451
		echo '
4452
	<sql>', $upcontext['error_string'], '</sql>';
4453
4454
	if ($is_debug)
4455
		echo '
4456
	<curtime>', time(), '</curtime>';
4457
}
4458
4459
// Template for the UTF-8 conversion step. Basically a copy of the backup stuff with slight modifications....
4460
function template_convert_utf8()
4461
{
4462
	global $upcontext, $support_js, $is_debug, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
4463
4464
	echo '
4465
				<h3>', $txt['upgrade_wait2'], '</h3>
4466
				<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4467
					<input type="hidden" name="utf8_done" id="utf8_done" value="0">
4468
					<strong>', $txt['upgrade_completed'], ' <span id="tab_done">', $upcontext['cur_table_num'], '</span> ', $txt['upgrade_outof'], ' ', $upcontext['table_count'], ' ', $txt['upgrade_tables'], '</strong>
4469
					<div id="debug_section">
4470
						<span id="debuginfo"></span>
4471
					</div>';
4472
4473
	// Done any tables so far?
4474 View Code Duplication
	if (!empty($upcontext['previous_tables']))
4475
		foreach ($upcontext['previous_tables'] as $table)
4476
			echo '
4477
					<br>', $txt['upgrade_completed_table'], ' &quot;', $table, '&quot;.';
4478
4479
	echo '
4480
					<h3 id="current_tab">
4481
						', $txt['upgrade_current_table'], ' &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;
4482
					</h3>';
4483
4484
	// If we dropped their index, let's let them know
4485
	if ($upcontext['dropping_index'])
4486
		echo '
4487
					<p id="indexmsg" style="font-weight: bold; font-style: italic; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">', $txt['upgrade_fulltext'], '</p>';
4488
4489
	// Completion notification
4490
	echo '
4491
					<p id="commess" style="display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">', $txt['upgrade_conversion_proceed'], '</p>';
4492
4493
	// Continue please!
4494
	$upcontext['continue'] = $support_js ? 2 : 1;
4495
4496
	// If javascript allows we want to do this using XML.
4497 View Code Duplication
	if ($support_js)
4498
	{
4499
		echo '
4500
					<script>
4501
						var lastTable = ', $upcontext['cur_table_num'], ';
4502
						function getNextTables()
4503
						{
4504
							getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onConversionUpdate);
4505
						}
4506
4507
						// Got an update!
4508
						function onConversionUpdate(oXMLDoc)
4509
						{
4510
							var sCurrentTableName = "";
4511
							var iTableNum = 0;
4512
							var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4513
							for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4514
								sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4515
							iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4516
4517
							// Update the page.
4518
							setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4519
							setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4520
							lastTable = iTableNum;
4521
							updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4522
4523
		// If debug flood the screen.
4524
		if ($is_debug)
4525
			echo '
4526
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4527
4528
						if (document.getElementById(\'debug_section\').scrollHeight)
4529
							document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4530
4531
		echo '
4532
						// Get the next update...
4533
						if (iTableNum == ', $upcontext['table_count'], ')
4534
						{
4535
							document.getElementById(\'commess\').style.display = "";
4536
							if (document.getElementById(\'indexmsg\') != null) {
4537
								document.getElementById(\'indexmsg\').style.display = "";
4538
							}
4539
							document.getElementById(\'current_tab\').style.display = "none";
4540
							document.getElementById(\'contbutt\').disabled = 0;
4541
							document.getElementById(\'utf8_done\').value = 1;
4542
						}
4543
						else
4544
							getNextTables();
4545
					}
4546
					getNextTables();
4547
				//# sourceURL=dynamicScript-conv.js
4548
				</script>';
4549
	}
4550
}
4551
4552
function template_convert_xml()
4553
{
4554
	global $upcontext;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
4555
4556
	echo '
4557
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4558
}
4559
4560
// Template for the database backup tool/
4561
function template_serialize_json()
4562
{
4563
	global $upcontext, $support_js, $is_debug, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
4564
4565
	echo '
4566
				<h3>', $txt['upgrade_convert_datajson'], '</h3>
4567
				<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4568
					<input type="hidden" name="json_done" id="json_done" value="0">
4569
					<strong>', $txt['upgrade_completed'], ' <span id="tab_done">', $upcontext['cur_table_num'], '</span> ', $txt['upgrade_outof'], ' ', $upcontext['table_count'], ' ', $txt['upgrade_tables'], '</strong>
4570
					<div id="debug_section">
4571
						<span id="debuginfo"></span>
4572
					</div>';
4573
4574
	// Dont any tables so far?
4575 View Code Duplication
	if (!empty($upcontext['previous_tables']))
4576
		foreach ($upcontext['previous_tables'] as $table)
4577
			echo '
4578
					<br>', $txt['upgrade_completed_table'], ' &quot;', $table, '&quot;.';
4579
4580
	echo '
4581
					<h3 id="current_tab">
4582
						', $txt['upgrade_current_table'], ' &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;
4583
					</h3>
4584
					<p id="commess" style="display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">', $txt['upgrade_json_completed'], '</p>';
4585
4586
	// Try to make sure substep was reset.
4587
	if ($upcontext['cur_table_num'] == $upcontext['table_count'])
4588
		echo '
4589
					<input type="hidden" name="substep" id="substep" value="0">';
4590
4591
	// Continue please!
4592
	$upcontext['continue'] = $support_js ? 2 : 1;
4593
4594
	// If javascript allows we want to do this using XML.
4595 View Code Duplication
	if ($support_js)
4596
	{
4597
		echo '
4598
					<script>
4599
						var lastTable = ', $upcontext['cur_table_num'], ';
4600
						function getNextTables()
4601
						{
4602
							getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4603
						}
4604
4605
						// Got an update!
4606
						function onBackupUpdate(oXMLDoc)
4607
						{
4608
							var sCurrentTableName = "";
4609
							var iTableNum = 0;
4610
							var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4611
							for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4612
								sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4613
							iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4614
4615
							// Update the page.
4616
							setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4617
							setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4618
							lastTable = iTableNum;
4619
							updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4620
4621
		// If debug flood the screen.
4622
		if ($is_debug)
4623
			echo '
4624
							setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>', $txt['upgrade_completed_table'], ' &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4625
4626
							if (document.getElementById(\'debug_section\').scrollHeight)
4627
								document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4628
4629
		echo '
4630
							// Get the next update...
4631
							if (iTableNum == ', $upcontext['table_count'], ')
4632
							{
4633
								document.getElementById(\'commess\').style.display = "";
4634
								document.getElementById(\'current_tab\').style.display = "none";
4635
								document.getElementById(\'contbutt\').disabled = 0;
4636
								document.getElementById(\'json_done\').value = 1;
4637
							}
4638
							else
4639
								getNextTables();
4640
						}
4641
						getNextTables();
4642
					//# sourceURL=dynamicScript-json.js
4643
					</script>';
4644
	}
4645
}
4646
4647
function template_serialize_json_xml()
4648
{
4649
	global $upcontext;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
4650
4651
	echo '
4652
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4653
}
4654
4655
function template_upgrade_complete()
4656
{
4657
	global $upcontext, $upgradeurl, $settings, $boardurl, $is_debug, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
4658
4659
	echo '
4660
				<h3>', $txt['upgrade_done'], ' <a href="', $boardurl, '/index.php">', $txt['upgrade_done2'], '</a>.  ', $txt['upgrade_done3'], '</h3>
4661
				<form action="', $boardurl, '/index.php">';
4662
4663
	if (!empty($upcontext['can_delete_script']))
4664
		echo '
4665
					<label>
4666
						<input type="checkbox" id="delete_self" onclick="doTheDelete(this);"> ', $txt['upgrade_delete_now'], '
4667
					</label>
4668
					<em>', $txt['upgrade_delete_server'], '</em>
4669
					<script>
4670
						function doTheDelete(theCheck)
4671
						{
4672
							var theImage = document.getElementById ? document.getElementById("delete_upgrader") : document.all.delete_upgrader;
4673
							theImage.src = "', $upgradeurl, '?delete=1&ts_" + (new Date().getTime());
4674
							theCheck.disabled = true;
4675
						}
4676
					</script>
4677
					<img src="', $settings['default_theme_url'], '/images/blank.png" alt="" id="delete_upgrader"><br>';
4678
4679
	$active = time() - $upcontext['started'];
4680
	$hours = floor($active / 3600);
4681
	$minutes = intval(($active / 60) % 60);
4682
	$seconds = intval($active % 60);
4683
4684
	if ($is_debug)
4685
	{
4686
		$totalTime = '';
4687 View Code Duplication
		if ($hours > 0)
4688
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
4689 View Code Duplication
		if ($minutes > 0)
4690
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
4691 View Code Duplication
		if ($seconds > 0)
4692
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
4693
	}
4694
4695
	if ($is_debug && !empty($totalTime))
4696
		echo '
4697
					<p> ', $txt['upgrade_completed_time'], ' ', $totalTime, '</p>';
4698
4699
	echo '
4700
					<p>
4701
						', sprintf($txt['upgrade_problems'], 'http://simplemachines.org'), '
4702
						<br>
4703
						', $txt['upgrade_luck'], '<br>
4704
						Simple Machines
4705
					</p>';
4706
}
4707
4708
/**
4709
 * Convert MySQL (var)char ip col to binary
4710
 *
4711
 * @param string $targetTable The table to perform the operation on
4712
 * @param string $oldCol The old column to gather data from
4713
 * @param string $newCol The new column to put data in
4714
 * @param int $limit The amount of entries to handle at once.
4715
 * @param int $setSize The amount of entries after which to update the database.
4716
 *
4717
 * newCol needs to be a varbinary(16) null able field
4718
 * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

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

Loading history...
4719
 */
4720
function MySQLConvertOldIp($targetTable, $oldCol, $newCol, $limit = 50000, $setSize = 100)
4721
{
4722
	global $smcFunc, $step_progress;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
4723
4724
	$current_substep = $_GET['substep'];
4725
4726
	if (empty($_GET['a']))
4727
		$_GET['a'] = 0;
4728
	$step_progress['name'] = 'Converting ips';
4729
	$step_progress['current'] = $_GET['a'];
4730
4731
	// Skip this if we don't have the column
4732
	$request = $smcFunc['db_query']('', '
4733
		SHOW FIELDS
4734
		FROM {db_prefix}{raw:table}
4735
		WHERE Field = {string:name}',
4736
		array(
4737
			'table' => $targetTable,
4738
			'name' => $oldCol,
4739
	));
4740
	if ($smcFunc['db_num_rows']($request) !== 1)
4741
	{
4742
		$smcFunc['db_free_result']($request);
4743
		return;
4744
	}
4745
	$smcFunc['db_free_result']($request);
4746
4747
	//mysql default max length is 1mb https://dev.mysql.com/doc/refman/5.1/en/packet-too-large.html
4748
	$arIp = array();
0 ignored issues
show
Unused Code introduced by
$arIp is not used, you could remove the assignment.

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

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

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

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

Loading history...
4749
4750
	$is_done = false;
4751
	while (!$is_done)
4752
	{
4753
		// Keep looping at the current step.
4754
		nextSubstep($current_substep);
4755
4756
		$arIp = array();
4757
4758
		$request = $smcFunc['db_query']('', '
4759
			SELECT DISTINCT {raw:old_col}
4760
			FROM {db_prefix}{raw:table_name}
4761
			WHERE {raw:new_col} IS NULL
4762
			LIMIT {int:limit}',
4763
			array(
4764
				'old_col' => $oldCol,
4765
				'new_col' => $newCol,
4766
				'table_name' => $targetTable,
4767
				'empty' => '',
4768
				'limit' => $limit,
4769
		));
4770
		while ($row = $smcFunc['db_fetch_assoc']($request))
4771
			$arIp[] = $row[$oldCol];
4772
		$smcFunc['db_free_result']($request);
4773
4774
		// Special case, null ip could keep us in a loop.
4775
		if (is_null($arIp[0]))
4776
			unset($arIp[0]);
4777
4778
		if (empty($arIp))
4779
			$is_done = true;
4780
4781
		$updates = array();
4782
		$cases = array();
4783
		$count = count($arIp);
4784
		for ($i = 0; $i < $count; $i++)
4785
		{
4786
			$arIp[$i] = trim($arIp[$i]);
4787
4788
			if (empty($arIp[$i]))
4789
				continue;
4790
4791
			$updates['ip' . $i] = $arIp[$i];
4792
			$cases[$arIp[$i]] = 'WHEN ' . $oldCol . ' = {string:ip' . $i . '} THEN {inet:ip' . $i . '}';
4793
4794
			if ($setSize > 0 && $i % $setSize === 0)
4795
			{
4796
				if (count($updates) == 1)
4797
					continue;
4798
4799
				$updates['whereSet'] = array_values($updates);
4800
				$smcFunc['db_query']('', '
4801
					UPDATE {db_prefix}' . $targetTable . '
4802
					SET ' . $newCol . ' = CASE ' .
4803
					implode('
4804
						', $cases) . '
4805
						ELSE NULL
4806
					END
4807
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4808
					$updates
4809
				);
4810
4811
				$updates = array();
4812
				$cases = array();
4813
			}
4814
		}
4815
4816
		// Incase some extras made it through.
4817
		if (!empty($updates))
4818
		{
4819
			if (count($updates) == 1)
4820
			{
4821
				foreach ($updates as $key => $ip)
4822
				{
4823
					$smcFunc['db_query']('', '
4824
						UPDATE {db_prefix}' . $targetTable . '
4825
						SET ' . $newCol . ' = {inet:ip}
4826
						WHERE ' . $oldCol . ' = {string:ip}',
4827
						array(
4828
							'ip' => $ip
4829
					));
4830
				}
4831
			}
4832
			else
4833
			{
4834
				$updates['whereSet'] = array_values($updates);
4835
				$smcFunc['db_query']('', '
4836
					UPDATE {db_prefix}' . $targetTable . '
4837
					SET ' . $newCol . ' = CASE ' .
4838
					implode('
4839
						', $cases) . '
4840
						ELSE NULL
4841
					END
4842
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4843
					$updates
4844
				);
4845
			}
4846
		}
4847
		else
4848
			$is_done = true;
4849
4850
		$_GET['a'] += $limit;
4851
		$step_progress['current'] = $_GET['a'];
4852
	}
4853
4854
	unset($_GET['a']);
4855
}
4856
4857
/**
4858
 * Get the column info. This is basically the same as smf_db_list_columns but we get 1 column, force detail and other checks.
4859
 *
4860
 * @param string $targetTable The table to perform the operation on
4861
 * @param string $column The column we are looking for.
4862
 *
4863
 * @return array Info on the table.
4864
 */
4865
function upgradeGetColumnInfo($targetTable, $column)
4866
{
4867
	global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
4868
4869
	// This should already be here, but be safe.
4870
	db_extend('packages');
4871
4872
	$columns = $smcFunc['db_list_columns']($targetTable, true);
4873
4874
	if (isset($columns[$column]))
4875
		return $columns[$column];
4876
	else
4877
		return null;
4878
}
4879
4880
?>