Completed
Pull Request — release-2.1 (#4470)
by Fran
08:53
created

upgrade.php ➔ DeleteUpgrade()   F

Complexity

Conditions 18
Paths 2304

Size

Total Lines 93
Code Lines 54

Duplication

Lines 2
Ratio 2.15 %

Importance

Changes 0
Metric Value
cc 18
eloc 54
nc 2304
nop 0
dl 2
loc 93
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines http://www.simplemachines.org
8
 * @copyright 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.2',
41
		'version_check' => '$version = pg_version(); return $version[\'client\'];',
42
		'always_has_db' => true,
43
	),
44
);
45
46
/**
47
 * The maximum time a single substep may take, in seconds.
48
 * @var int
49
 */
50
$timeLimitThreshold = 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
// All the steps in detail.
86
// Number,Name,Function,Progress Weight.
87
$upcontext['steps'] = array(
88
	0 => array(1, $txt['upgrade_step_login'], 'WelcomeLogin', 2),
89
	1 => array(2, $txt['upgrade_step_options'], 'UpgradeOptions', 2),
90
	2 => array(3, $txt['upgrade_step_backup'], 'BackupDatabase', 10),
91
	3 => array(4, $txt['upgrade_step_database'], 'DatabaseChanges', 50),
92
	4 => array(5, $txt['upgrade_step_convertutf'], 'ConvertUtf8', 20),
93
	5 => array(6, $txt['upgrade_step_convertjson'], 'serialize_to_json', 10),
94
	6 => array(7, $txt['upgrade_step_delete'], 'DeleteUpgrade', 1),
95
);
96
// Just to remember which one has files in it.
97
$upcontext['database_step'] = 3;
98
@set_time_limit(600);
99
if (!ini_get('safe_mode'))
100
{
101
	ini_set('mysql.connect_timeout', -1);
102
	ini_set('default_socket_timeout', 900);
103
}
104
// Clean the upgrade path if this is from the client.
105
if (!empty($_SERVER['argv']) && php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
106
	for ($i = 1; $i < $_SERVER['argc']; $i++)
107
	{
108
		if (preg_match('~^--path=(.+)$~', $_SERVER['argv'][$i], $match) != 0)
109
			$upgrade_path = substr($match[1], -1) == '/' ? substr($match[1], 0, -1) : $match[1];
110
	}
111
112
// Are we from the client?
113
if (php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
114
{
115
	$command_line = true;
116
	$disable_security = true;
117
}
118
else
119
	$command_line = false;
120
121
// Load this now just because we can.
122
require_once($upgrade_path . '/Settings.php');
123
124
// We don't use "-utf8" anymore...  Tweak the entry that may have been loaded by Settings.php
125
if (isset($language))
126
	$language = str_ireplace('-utf8', '', $language);
127
128
// Are we logged in?
129
if (isset($upgradeData))
130
{
131
	$upcontext['user'] = json_decode(base64_decode($upgradeData), true);
132
133
	// Check for sensible values.
134
	if (empty($upcontext['user']['started']) || $upcontext['user']['started'] < time() - 86400)
135
		$upcontext['user']['started'] = time();
136
	if (empty($upcontext['user']['updated']) || $upcontext['user']['updated'] < time() - 86400)
137
		$upcontext['user']['updated'] = 0;
138
139
	$upcontext['started'] = $upcontext['user']['started'];
140
	$upcontext['updated'] = $upcontext['user']['updated'];
141
142
	$is_debug = !empty($upcontext['user']['debug']) ? true : false;
143
}
144
145
// Nothing sensible?
146
if (empty($upcontext['updated']))
147
{
148
	$upcontext['started'] = time();
149
	$upcontext['updated'] = 0;
150
	$upcontext['user'] = array(
151
		'id' => 0,
152
		'name' => 'Guest',
153
		'pass' => 0,
154
		'started' => $upcontext['started'],
155
		'updated' => $upcontext['updated'],
156
	);
157
}
158
159
// Load up some essential data...
160
loadEssentialData();
161
162
// Are we going to be mimic'ing SSI at this point?
163
if (isset($_GET['ssi']))
164
{
165
	require_once($sourcedir . '/Errors.php');
166
	require_once($sourcedir . '/Logging.php');
167
	require_once($sourcedir . '/Load.php');
168
	require_once($sourcedir . '/Security.php');
169
	require_once($sourcedir . '/Subs-Package.php');
170
171
	// SMF isn't started up properly, but loadUserSettings calls our cookies.
172
	if (!isset($smcFunc['json_encode']))
173
	{
174
		$smcFunc['json_encode'] = 'json_encode';
175
		$smcFunc['json_decode'] = 'smf_json_decode';
176
	}
177
178
	loadUserSettings();
179
	loadPermissions();
180
}
181
182
// Include our helper functions.
183
require_once($sourcedir . '/Subs.php');
184
require_once($sourcedir . '/LogInOut.php');
185
186
// This only exists if we're on SMF ;)
187
if (isset($modSettings['smfVersion']))
188
{
189
	$request = $smcFunc['db_query']('', '
190
		SELECT variable, value
191
		FROM {db_prefix}themes
192
		WHERE id_theme = {int:id_theme}
193
			AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
194
		array(
195
			'id_theme' => 1,
196
			'theme_url' => 'theme_url',
197
			'theme_dir' => 'theme_dir',
198
			'images_url' => 'images_url',
199
			'db_error_skip' => true,
200
		)
201
	);
202
	while ($row = $smcFunc['db_fetch_assoc']($request))
203
		$modSettings[$row['variable']] = $row['value'];
204
	$smcFunc['db_free_result']($request);
205
}
206
207
if (!isset($modSettings['theme_url']))
208
{
209
	$modSettings['theme_dir'] = $boarddir . '/Themes/default';
210
	$modSettings['theme_url'] = 'Themes/default';
211
	$modSettings['images_url'] = 'Themes/default/images';
212
}
213
if (!isset($settings['default_theme_url']))
214
	$settings['default_theme_url'] = $modSettings['theme_url'];
215
if (!isset($settings['default_theme_dir']))
216
	$settings['default_theme_dir'] = $modSettings['theme_dir'];
217
218
$upcontext['is_large_forum'] = (empty($modSettings['smfVersion']) || $modSettings['smfVersion'] <= '1.1 RC1') && !empty($modSettings['totalMessages']) && $modSettings['totalMessages'] > 75000;
219
// Default title...
220
$upcontext['page_title'] = 'Updating Your SMF Installation!';
221
222
// Have we got tracking data - if so use it (It will be clean!)
223
if (isset($_GET['data']))
224
{
225
	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...
226
227
	$upcontext['upgrade_status'] = json_decode(base64_decode($_GET['data']), true);
228
	$upcontext['current_step'] = $upcontext['upgrade_status']['curstep'];
229
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
230
	$upcontext['rid'] = $upcontext['upgrade_status']['rid'];
231
	$support_js = $upcontext['upgrade_status']['js'];
232
233
	// Only set this if the upgrader status says so.
234
	if (empty($is_debug))
235
		$is_debug = $upcontext['upgrade_status']['debug'];
236
237
	// Load the language.
238
	if (file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
239
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
240
}
241
// Set the defaults.
242
else
243
{
244
	$upcontext['current_step'] = 0;
245
	$upcontext['rid'] = mt_rand(0, 5000);
246
	$upcontext['upgrade_status'] = array(
247
		'curstep' => 0,
248
		'lang' => isset($_GET['lang']) ? $_GET['lang'] : basename($language, '.lng'),
249
		'rid' => $upcontext['rid'],
250
		'pass' => 0,
251
		'debug' => 0,
252
		'js' => 0,
253
	);
254
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
255
}
256
257
// If this isn't the first stage see whether they are logging in and resuming.
258
if ($upcontext['current_step'] != 0 || !empty($upcontext['user']['step']))
259
	checkLogin();
260
261
if ($command_line)
262
	cmdStep0();
263
264
// Don't error if we're using xml.
265
if (isset($_GET['xml']))
266
	$upcontext['return_error'] = true;
267
268
// Loop through all the steps doing each one as required.
269
$upcontext['overall_percent'] = 0;
270
foreach ($upcontext['steps'] as $num => $step)
271
{
272
	if ($num >= $upcontext['current_step'])
273
	{
274
		// The current weight of this step in terms of overall progress.
275
		$upcontext['step_weight'] = $step[3];
276
		// Make sure we reset the skip button.
277
		$upcontext['skip'] = false;
278
279
		// We cannot proceed if we're not logged in.
280
		if ($num != 0 && !$disable_security && $upcontext['user']['pass'] != $upcontext['upgrade_status']['pass'])
281
		{
282
			$upcontext['steps'][0][2]();
283
			break;
284
		}
285
286
		// Call the step and if it returns false that means pause!
287
		if (function_exists($step[2]) && $step[2]() === false)
288
			break;
289
		elseif (function_exists($step[2])) {
290
			//Start each new step with this unset, so the 'normal' template is called first
291
			unset($_GET['xml']);
292
			//Clear out warnings at the start of each step
293
			unset($upcontext['custom_warning']);
294
			$_GET['substep'] = 0;
295
			$upcontext['current_step']++;
296
		}
297
	}
298
	$upcontext['overall_percent'] += $step[3];
299
}
300
301
upgradeExit();
302
303
// Exit the upgrade script.
304
function upgradeExit($fallThrough = false)
305
{
306
	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...
307
308
	// Save where we are...
309
	if (!empty($upcontext['current_step']) && !empty($upcontext['user']['id']))
310
	{
311
		$upcontext['user']['step'] = $upcontext['current_step'];
312
		$upcontext['user']['substep'] = $_GET['substep'];
313
		$upcontext['user']['updated'] = time();
314
		$upcontext['debug'] = $is_debug;
315
		$upgradeData = base64_encode(json_encode($upcontext['user']));
316
		require_once($sourcedir . '/Subs-Admin.php');
317
		updateSettingsFile(array('upgradeData' => '"' . $upgradeData . '"'));
318
		updateDbLastError(0);
319
	}
320
321
	// Handle the progress of the step, if any.
322
	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
323
	{
324
		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
325
		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
326
	}
327
	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
328
329
	// We usually dump our templates out.
330
	if (!$fallThrough)
331
	{
332
		// This should not happen my dear... HELP ME DEVELOPERS!!
333
		if (!empty($command_line))
334
		{
335
			if (function_exists('debug_print_backtrace'))
336
				debug_print_backtrace();
337
338
			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.';
339
			flush();
340
			die();
341
		}
342
343
		if (!isset($_GET['xml']))
344
			template_upgrade_above();
345
		else
346
		{
347
			header('Content-Type: text/xml; charset=UTF-8');
348
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
349
			$upcontext['get_data'] = array();
350
			foreach ($_GET as $k => $v)
351
			{
352
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
353
				{
354
					$upcontext['get_data'][$k] = $v;
355
				}
356
			}
357
			template_xml_above();
358
		}
359
360
		// Call the template.
361
		if (isset($upcontext['sub_template']))
362
		{
363
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
364
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(json_encode($upcontext['upgrade_status']));
365
366
			// Custom stuff to pass back?
367
			if (!empty($upcontext['query_string']))
368
				$upcontext['form_url'] .= $upcontext['query_string'];
369
370
			// Call the appropriate subtemplate
371
			if (is_callable('template_' . $upcontext['sub_template']))
372
				call_user_func('template_' . $upcontext['sub_template']);
373
			else
374
				die('Upgrade aborted!  Invalid template: template_' . $upcontext['sub_template']);
375
		}
376
377
		// Was there an error?
378
		if (!empty($upcontext['forced_error_message']))
379
			echo $upcontext['forced_error_message'];
380
381
		// Show the footer.
382
		if (!isset($_GET['xml']))
383
			template_upgrade_below();
384
		else
385
			template_xml_below();
386
	}
387
388
389
	if (!empty($command_line) && $is_debug)
390
	{
391
		$active = time() - $upcontext['started'];
392
		$hours = floor($active / 3600);
393
		$minutes = intval(($active / 60) % 60);
394
		$seconds = intval($active % 60);
395
396
		$totalTime = '';
397
		if ($hours > 0)
398
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
399
		if ($minutes > 0)
400
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
401
		if ($seconds > 0)
402
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
403
404
		if (!empty($totalTime))
405
			echo "\n" . '', $txt['upgrade_completed_time'], ' ' . $totalTime . "\n";
406
	}
407
408
	// Bang - gone!
409
	die();
410
}
411
// Load the list of language files, and the current language file.
412
function load_lang_file()
413
{
414
	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...
415
416
	$incontext['detected_languages'] = array();
417
418
	// Make sure the languages directory actually exists.
419
	if (file_exists(dirname(__FILE__) . '/Themes/default/languages'))
420
	{
421
		// Find all the "Install" language files in the directory.
422
		$dir = dir(dirname(__FILE__) . '/Themes/default/languages');
423
		while ($entry = $dir->read())
424
		{
425
			if (substr($entry, 0, 8) == 'Install.' && substr($entry, -4) == '.php')
426
				$incontext['detected_languages'][$entry] = ucfirst(substr($entry, 8, strlen($entry) - 12));
427
		}
428
		$dir->close();
429
	}
430
431
	// Didn't find any, show an error message!
432
	if (empty($incontext['detected_languages']))
433
	{
434
		// Let's not cache this message, eh?
435
		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
436
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
437
		header('Cache-Control: no-cache');
438
439
		echo '<!DOCTYPE html>
440
<html>
441
	<head>
442
		<title>SMF Upgrader: Error!</title>
443
	</head>
444
	<body style="font-family: sans-serif;"><div style="width: 600px;">
445
		<h1 style="font-size: 14pt;">A critical error has occurred.</h1>
446
447
		<p>This upgrader was unable to find the upgrader\'s language file or files.  They should be found under:</p>
448
449
		<div style="margin: 1ex; font-family: monospace; font-weight: bold;">', dirname($_SERVER['PHP_SELF']) != '/' ? dirname($_SERVER['PHP_SELF']) : '', '/Themes/default/languages</div>
450
451
		<p>In some cases, FTP clients do not properly upload files with this many folders.  Please double check to make sure you <span style="font-weight: 600;">have uploaded all the files in the distribution</span>.</p>
452
		<p>If that doesn\'t help, please make sure this install.php file is in the same place as the Themes folder.</p>
453
454
		<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>
455
	</div></body>
456
</html>';
457
		die;
458
	}
459
460
// Used to direct the user to another location.
461
function redirectLocation($location, $addForm = true)
462
{
463
	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...
464
465
	// Command line users can't be redirected.
466
	if ($command_line)
467
		upgradeExit(true);
468
469
	// Are we providing the core info?
470
	if ($addForm)
471
	{
472
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
473
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
474
	}
475
476
	while (@ob_end_clean());
477
	header('Location: ' . strtr($location, array('&amp;' => '&')));
478
479
	// Exit - saving status as we go.
480
	upgradeExit(true);
481
}
482
483
// Load all essential data and connect to the DB as this is pre SSI.php
484
function loadEssentialData()
485
{
486
	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...
487
	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...
488
489
	error_reporting(E_ALL);
490
	define('SMF', 1);
491
492
	// Start the session.
493
	if (@ini_get('session.save_handler') == 'user')
494
		@ini_set('session.save_handler', 'files');
495
	@session_start();
496
497
	if (empty($smcFunc))
498
		$smcFunc = array();
499
500
	// We need this for authentication and some upgrade code
501
	require_once($sourcedir . '/Subs-Auth.php');
502
	require_once($sourcedir . '/Class-Package.php');
503
504
	$smcFunc['strtolower'] = 'smf_strtolower';
505
506
	// Initialize everything...
507
	initialize_inputs();
508
509
	// Get the database going!
510
	if (empty($db_type) || $db_type == 'mysqli')
511
	{
512
		$db_type = 'mysql';
513
		// If overriding $db_type, need to set its settings.php entry too
514
		$changes = array();
515
		$changes['db_type'] = '\'mysql\'';
516
		require_once($sourcedir . '/Subs-Admin.php');
517
		updateSettingsFile($changes);
518
	}
519
520
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
521
	{
522
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
523
524
		// Make the connection...
525
		if (empty($db_connection))
526
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, array('non_fatal' => true));
527
		else
528
			// If we've returned here, ping/reconnect to be safe
529
			$smcFunc['db_ping']($db_connection);
530
531
		// Oh dear god!!
532
		if ($db_connection === null)
533
			die('Unable to connect to database - please check username and password are correct in Settings.php');
534
535
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
536
			$smcFunc['db_query']('', '
537
			SET NAMES {string:db_character_set}',
538
			array(
539
				'db_error_skip' => true,
540
				'db_character_set' => $db_character_set,
541
			)
542
		);
543
544
		// Load the modSettings data...
545
		$request = $smcFunc['db_query']('', '
546
			SELECT variable, value
547
			FROM {db_prefix}settings',
548
			array(
549
				'db_error_skip' => true,
550
			)
551
		);
552
		$modSettings = array();
553
		while ($row = $smcFunc['db_fetch_assoc']($request))
554
			$modSettings[$row['variable']] = $row['value'];
555
		$smcFunc['db_free_result']($request);
556
	}
557
	else
558
	{
559
		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.');
560
	}
561
562
	require_once($sourcedir . '/Subs.php');
563
564
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
565
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
566
	{
567
		require_once($sourcedir . '/QueryString.php');
568
		cleanRequest();
569
	}
570
571
	if (!isset($_GET['substep']))
572
		$_GET['substep'] = 0;
573
}
574
575
function initialize_inputs()
576
{
577
	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...
578
579
	$start_time = time();
580
581
	umask(0);
582
583
	ob_start();
584
585
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
586
	ignore_user_abort(true);
587
588
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
589
	if (isset($_GET['delete']))
590
	{
591
		@unlink(__FILE__);
592
593
		// And the extra little files ;).
594
		@unlink(dirname(__FILE__) . '/upgrade_1-0.sql');
595
		@unlink(dirname(__FILE__) . '/upgrade_1-1.sql');
596
		@unlink(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
597
		@unlink(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
598
		@unlink(dirname(__FILE__) . '/upgrade-helper.php');
599
600
		$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...
601
		while ($file = readdir($dh))
602
		{
603
			if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
604
				@unlink(dirname(__FILE__) . '/' . $file);
605
		}
606
		closedir($dh);
607
608
		// Legacy files while we're at it. NOTE: We only touch files we KNOW shouldn't be there.
609
		// 1.1 Sources files not in 2.0+
610
		@unlink(dirname(__FILE__) . '/Sources/ModSettings.php');
611
		// 1.1 Templates that don't exist any more (e.g. renamed)
612
		@unlink(dirname(__FILE__) . '/Themes/default/Combat.template.php');
613
		@unlink(dirname(__FILE__) . '/Themes/default/Modlog.template.php');
614
		// 1.1 JS files were stored in the main theme folder, but in 2.0+ are in the scripts/ folder
615
		@unlink(dirname(__FILE__) . '/Themes/default/fader.js');
616
		@unlink(dirname(__FILE__) . '/Themes/default/script.js');
617
		@unlink(dirname(__FILE__) . '/Themes/default/spellcheck.js');
618
		@unlink(dirname(__FILE__) . '/Themes/default/xml_board.js');
619
		@unlink(dirname(__FILE__) . '/Themes/default/xml_topic.js');
620
621
		// 2.0 Sources files not in 2.1+
622
		@unlink(dirname(__FILE__) . '/Sources/DumpDatabase.php');
623
		@unlink(dirname(__FILE__) . '/Sources/LockTopic.php');
624
625
		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');
626
		exit;
627
	}
628
629
	// Something is causing this to happen, and it's annoying.  Stop it.
630
	$temp = 'upgrade_php?step';
631
	while (strlen($temp) > 4)
632
	{
633
		if (isset($_GET[$temp]))
634
			unset($_GET[$temp]);
635
		$temp = substr($temp, 1);
636
	}
637
638
	// Force a step, defaulting to 0.
639
	$_GET['step'] = (int) @$_GET['step'];
640
	$_GET['substep'] = (int) @$_GET['substep'];
641
}
642
643
// Step 0 - Let's welcome them in and ask them to login!
644
function WelcomeLogin()
645
{
646
	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...
647
	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...
648
649
	// We global $txt here so that the language files can add to them. This variable is NOT unused.
650
	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...
651
652
	$upcontext['sub_template'] = 'welcome_message';
653
654
	// Check for some key files - one template, one language, and a new and an old source file.
655
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
656
		&& @file_exists($sourcedir . '/QueryString.php')
657
		&& @file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')
658
		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
659
660
	// Need legacy scripts?
661
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.1)
662
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
663
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.0)
664
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-1.sql');
665
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 1.1)
666
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
667
668
	// We don't need "-utf8" files anymore...
669
	$upcontext['language'] = str_ireplace('-utf8', '', $upcontext['language']);
670
671
	// This needs to exist!
672
	if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
673
		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>]');
674
	else
675
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
676
677
	if (!$check)
678
		// 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.
679
		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.');
680
681
	// Do they meet the install requirements?
682
	if (!php_version_check())
683
		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.');
684
685
	if (!db_version_check())
686
		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.');
687
688
	// Do some checks to make sure they have proper privileges
689
	db_extend('packages');
690
691
	// CREATE
692
	$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');
693
694
	// ALTER
695
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
696
697
	// DROP
698
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
699
700
	// Sorry... we need CREATE, ALTER and DROP
701
	if (!$create || !$alter || !$drop)
702
		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.');
703
704
	// Do a quick version spot check.
705
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
706
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
707
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
708
		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.');
709
710
	// What absolutely needs to be writable?
711
	$writable_files = array(
712
		$boarddir . '/Settings.php',
713
		$boarddir . '/Settings_bak.php',
714
		$boarddir . '/db_last_error.php',
715
		$modSettings['theme_dir'] . '/css/minified.css',
716
		$modSettings['theme_dir'] . '/scripts/minified.js',
717
		$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
718
	);
719
720
	// Do we need to add this setting?
721
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
722
723
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] . '/custom_avatar';
724
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
725
726
	// This little fellow has to cooperate...
727
	quickFileWritable($custom_av_dir);
728
729
	// Are we good now?
730
	if (!is_writable($custom_av_dir))
731
		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));
732
	elseif ($need_settings_update)
733
	{
734
		if (!function_exists('cache_put_data'))
735
			require_once($sourcedir . '/Load.php');
736
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
737
		updateSettings(array('custom_avatar_url' => $custom_av_url));
738
	}
739
740
	require_once($sourcedir . '/Security.php');
741
742
	// Check the cache directory.
743
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
744
	if (!file_exists($cachedir_temp))
745
		@mkdir($cachedir_temp);
746
	if (!file_exists($cachedir_temp))
747
		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.');
748
749
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
750
		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>.');
751
	elseif (!isset($_GET['skiplang']))
752
	{
753
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
754
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
755
756
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
757
			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>]');
758
	}
759
760
	if (!makeFilesWritable($writable_files))
761
		return false;
762
763
	// Check agreement.txt. (it may not exist, in which case $boarddir must be writable.)
764
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
765
		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.');
766
767
	// Upgrade the agreement.
768
	elseif (isset($modSettings['agreement']))
769
	{
770
		$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...
771
		fwrite($fp, $modSettings['agreement']);
772
		fclose($fp);
773
	}
774
775
	// We're going to check that their board dir setting is right in case they've been moving stuff around.
776
	if (strtr($boarddir, array('/' => '', '\\' => '')) != strtr(dirname(__FILE__), array('/' => '', '\\' => '')))
777
		$upcontext['warning'] = '
778
			It looks as if your board directory settings <em>might</em> be incorrect. Your board directory is currently set to &quot;' . $boarddir . '&quot; but should probably be &quot;' . dirname(__FILE__) . '&quot;. Settings.php currently lists your paths as:<br>
779
			<ul>
780
				<li>Board Directory: ' . $boarddir . '</li>
781
				<li>Source Directory: ' . $boarddir . '</li>
782
				<li>Cache Directory: ' . $cachedir_temp . '</li>
783
			</ul>
784
			If these seem incorrect please open Settings.php in a text editor before proceeding with this upgrade. If they are incorrect due to you moving your forum to a new location please download and execute the <a href="https://download.simplemachines.org/?tools">Repair Settings</a> tool from the Simple Machines website before continuing.';
785
786
	// Confirm mbstring is loaded...
787
	if (!extension_loaded('mbstring'))
788
		return throw_error($txt['install_no_mbstring']);
789
790
	// Check for https stream support.
791
	$supported_streams = stream_get_wrappers();
792
	if (!in_array('https', $supported_streams))
793
		$upcontext['custom_warning'] = $txt['install_no_https'];
794
795
	// Either we're logged in or we're going to present the login.
796
	if (checkLogin())
797
		return true;
798
799
	$upcontext += createToken('login');
800
801
	return false;
802
}
803
804
// Step 0.5: Does the login work?
805
function checkLogin()
806
{
807
	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...
808
	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...
809
810
	// Don't bother if the security is disabled.
811
	if ($disable_security)
812
		return true;
813
814
	// Are we trying to login?
815
	if (isset($_POST['contbutt']) && (!empty($_POST['user'])))
816
	{
817
		// If we've disabled security pick a suitable name!
818
		if (empty($_POST['user']))
819
			$_POST['user'] = 'Administrator';
820
821
		// Before 2.0 these column names were different!
822
		$oldDB = false;
823
		if (empty($db_type) || $db_type == 'mysql')
824
		{
825
			$request = $smcFunc['db_query']('', '
826
				SHOW COLUMNS
827
				FROM {db_prefix}members
828
				LIKE {string:member_name}',
829
				array(
830
					'member_name' => 'memberName',
831
					'db_error_skip' => true,
832
				)
833
			);
834
			if ($smcFunc['db_num_rows']($request) != 0)
835
				$oldDB = true;
836
			$smcFunc['db_free_result']($request);
837
		}
838
839
		// Get what we believe to be their details.
840
		if (!$disable_security)
841
		{
842
			if ($oldDB)
843
				$request = $smcFunc['db_query']('', '
844
					SELECT id_member, memberName AS member_name, passwd, id_group,
845
					additionalGroups AS additional_groups, lngfile
846
					FROM {db_prefix}members
847
					WHERE memberName = {string:member_name}',
848
					array(
849
						'member_name' => $_POST['user'],
850
						'db_error_skip' => true,
851
					)
852
				);
853
			else
854
				$request = $smcFunc['db_query']('', '
855
					SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile
856
					FROM {db_prefix}members
857
					WHERE member_name = {string:member_name}',
858
					array(
859
						'member_name' => $_POST['user'],
860
						'db_error_skip' => true,
861
					)
862
				);
863
			if ($smcFunc['db_num_rows']($request) != 0)
864
			{
865
				list ($id_member, $name, $password, $id_group, $addGroups, $user_language) = $smcFunc['db_fetch_row']($request);
866
867
				$groups = explode(',', $addGroups);
868
				$groups[] = $id_group;
869
870
				foreach ($groups as $k => $v)
871
					$groups[$k] = (int) $v;
872
873
				$sha_passwd = sha1(strtolower($name) . un_htmlspecialchars($_REQUEST['passwrd']));
874
875
				// We don't use "-utf8" anymore...
876
				$user_language = str_ireplace('-utf8', '', $user_language);
877
			}
878
			else
879
				$upcontext['username_incorrect'] = true;
880
			$smcFunc['db_free_result']($request);
881
		}
882
		$upcontext['username'] = $_POST['user'];
883
884
		// Track whether javascript works!
885
		if (!empty($_POST['js_works']))
886
		{
887
			$upcontext['upgrade_status']['js'] = 1;
888
			$support_js = 1;
889
		}
890
		else
891
			$support_js = 0;
892
893
		// Note down the version we are coming from.
894
		if (!empty($modSettings['smfVersion']) && empty($upcontext['user']['version']))
895
			$upcontext['user']['version'] = $modSettings['smfVersion'];
896
897
		// Didn't get anywhere?
898
		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']))
899
		{
900
			// MD5?
901
			$md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user']));
902
			if ($md5pass != $password)
903
			{
904
				$upcontext['password_failed'] = true;
905
				// Disable the hashing this time.
906
				$upcontext['disable_login_hashing'] = true;
907
			}
908
		}
909
910
		if ((empty($upcontext['password_failed']) && !empty($name)) || $disable_security)
911
		{
912
			// Set the password.
913
			if (!$disable_security)
914
			{
915
				// Do we actually have permission?
916
				if (!in_array(1, $groups))
917
				{
918
					$request = $smcFunc['db_query']('', '
919
						SELECT permission
920
						FROM {db_prefix}permissions
921
						WHERE id_group IN ({array_int:groups})
922
							AND permission = {string:admin_forum}',
923
						array(
924
							'groups' => $groups,
925
							'admin_forum' => 'admin_forum',
926
							'db_error_skip' => true,
927
						)
928
					);
929
					if ($smcFunc['db_num_rows']($request) == 0)
930
						return throw_error('You need to be an admin to perform an upgrade!');
931
					$smcFunc['db_free_result']($request);
932
				}
933
934
				$upcontext['user']['id'] = $id_member;
935
				$upcontext['user']['name'] = $name;
936
			}
937
			else
938
			{
939
				$upcontext['user']['id'] = 1;
940
				$upcontext['user']['name'] = 'Administrator';
941
			}
942
			$upcontext['user']['pass'] = mt_rand(0, 60000);
943
			// This basically is used to match the GET variables to Settings.php.
944
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
945
946
			// Set the language to that of the user?
947
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
948
			{
949
				$user_language = basename($user_language, '.lng');
950
				$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
951
				preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
952
953
				if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
954
					$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'] . '.';
955
				elseif (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . basename($user_language, '.lng') . '.php'))
956
					$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'] . '.';
957
				else
958
				{
959
					// Set this as the new language.
960
					$upcontext['language'] = $user_language;
961
					$upcontext['upgrade_status']['lang'] = $upcontext['language'];
962
963
					// Include the file.
964
					require_once($modSettings['theme_dir'] . '/languages/Install.' . $user_language . '.php');
965
				}
966
			}
967
968
			// If we're resuming set the step and substep to be correct.
969
			if (isset($_POST['cont']))
970
			{
971
				$upcontext['current_step'] = $upcontext['user']['step'];
972
				$_GET['substep'] = $upcontext['user']['substep'];
973
			}
974
975
			return true;
976
		}
977
	}
978
979
	return false;
980
}
981
982
// Step 1: Do the maintenance and backup.
983
function UpgradeOptions()
984
{
985
	global $db_prefix, $command_line, $modSettings, $is_debug, $smcFunc, $packagesdir, $tasksdir, $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...
986
	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...
987
988
	$upcontext['sub_template'] = 'upgrade_options';
989
	$upcontext['page_title'] = 'Upgrade Options';
990
991
	db_extend('packages');
992
	$upcontext['karma_installed'] = array('good' => false, 'bad' => false);
993
	$member_columns = $smcFunc['db_list_columns']('{db_prefix}members');
994
995
	$upcontext['karma_installed']['good'] = in_array('karma_good', $member_columns);
996
	$upcontext['karma_installed']['bad'] = in_array('karma_bad', $member_columns);
997
998
	unset($member_columns);
999
1000
	// If we've not submitted then we're done.
1001
	if (empty($_POST['upcont']))
1002
		return false;
1003
1004
	// Firstly, if they're enabling SM stat collection just do it.
1005
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']))
1006
	{
1007
		$upcontext['allow_sm_stats'] = true;
1008
1009
		// Don't register if we still have a key.
1010
		if (empty($modSettings['sm_stats_key']))
1011
		{
1012
			// Attempt to register the site etc.
1013
			$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...
1014
			if ($fp)
1015
			{
1016
				$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
1017
				$out .= 'Host: www.simplemachines.org' . "\r\n";
1018
				$out .= 'Connection: Close' . "\r\n\r\n";
1019
				fwrite($fp, $out);
1020
1021
				$return_data = '';
1022
				while (!feof($fp))
1023
					$return_data .= fgets($fp, 128);
1024
1025
				fclose($fp);
1026
1027
				// Get the unique site ID.
1028
				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...
1029
1030
				if (!empty($ID[1]))
1031
					$smcFunc['db_insert']('replace',
1032
						$db_prefix . 'settings',
1033
						array('variable' => 'string', 'value' => 'string'),
1034
						array(
1035
							array('sm_stats_key', $ID[1]),
1036
							array('enable_sm_stats', 1),
1037
						),
1038
						array('variable')
1039
					);
1040
			}
1041
		}
1042
		else
1043
		{
1044
			$smcFunc['db_insert']('replace',
1045
				$db_prefix . 'settings',
1046
				array('variable' => 'string', 'value' => 'string'),
1047
				array('enable_sm_stats', 1),
1048
				array('variable')
1049
			);
1050
		}
1051
	}
1052
	// Don't remove stat collection unless we unchecked the box for real, not from the loop.
1053
	elseif (empty($_POST['stats']) && empty($upcontext['allow_sm_stats']))
1054
		$smcFunc['db_query']('', '
1055
			DELETE FROM {db_prefix}settings
1056
			WHERE variable = {string:enable_sm_stats}',
1057
			array(
1058
				'enable_sm_stats' => 'enable_sm_stats',
1059
				'db_error_skip' => true,
1060
			)
1061
		);
1062
1063
	// Deleting old karma stuff?
1064
	if (!empty($_POST['delete_karma']))
1065
	{
1066
		// Delete old settings vars.
1067
		$smcFunc['db_query']('', '
1068
			DELETE FROM {db_prefix}settings
1069
			WHERE variable IN ({array_string:karma_vars})',
1070
			array(
1071
				'karma_vars' => array('karmaMode', 'karmaTimeRestrictAdmins', 'karmaWaitTime', 'karmaMinPosts', 'karmaLabel', 'karmaSmiteLabel', 'karmaApplaudLabel'),
1072
			)
1073
		);
1074
1075
		// Cleaning up old karma member settings.
1076
		if ($upcontext['karma_installed']['good'])
1077
			$smcFunc['db_query']('', '
1078
				ALTER TABLE {db_prefix}members
1079
				DROP karma_good',
1080
				array()
1081
			);
1082
1083
		// Does karma bad was enable?
1084
		if ($upcontext['karma_installed']['bad'])
1085
			$smcFunc['db_query']('', '
1086
				ALTER TABLE {db_prefix}members
1087
				DROP karma_bad',
1088
				array()
1089
			);
1090
1091
		// Cleaning up old karma permissions.
1092
		$smcFunc['db_query']('', '
1093
			DELETE FROM {db_prefix}permissions
1094
			WHERE permission = {string:karma_vars}',
1095
			array(
1096
				'karma_vars' => 'karma_edit',
1097
			)
1098
		);
1099
		// Cleaning up old log_karma table
1100
		$smcFunc['db_query']('', '
1101
			DROP TABLE IF EXISTS {db_prefix}log_karma',
1102
			array()
1103
		);
1104
	}
1105
1106
	// Emptying the error log?
1107
	if (!empty($_POST['empty_error']))
1108
		$smcFunc['db_query']('truncate_table', '
1109
			TRUNCATE {db_prefix}log_errors',
1110
			array(
1111
			)
1112
		);
1113
1114
	$changes = array();
1115
1116
	// Add proxy settings.
1117
	if (!isset($GLOBALS['image_proxy_maxsize']))
1118
		$changes += array(
1119
			'image_proxy_secret' => '\'' . substr(sha1(mt_rand()), 0, 20) . '\'',
1120
			'image_proxy_maxsize' => 5190,
1121
			'image_proxy_enabled' => 0,
1122
		);
1123
1124
	// If $boardurl reflects https, set force_ssl
1125
	if (!function_exists('cache_put_data'))
1126
		require_once($sourcedir . '/Load.php');
1127
	if (stripos($boardurl, 'https://') !== false)
1128
		updateSettings(array('force_ssl' => '2'));
1129
1130
	// If we're overriding the language follow it through.
1131
	if (isset($_GET['lang']) && file_exists($modSettings['theme_dir'] . '/languages/index.' . $_GET['lang'] . '.php'))
1132
		$changes['language'] = '\'' . $_GET['lang'] . '\'';
1133
1134
	if (!empty($_POST['maint']))
1135
	{
1136
		$changes['maintenance'] = '2';
1137
		// Remember what it was...
1138
		$upcontext['user']['main'] = $maintenance;
1139
1140
		if (!empty($_POST['maintitle']))
1141
		{
1142
			$changes['mtitle'] = '\'' . addslashes($_POST['maintitle']) . '\'';
1143
			$changes['mmessage'] = '\'' . addslashes($_POST['mainmessage']) . '\'';
1144
		}
1145
		else
1146
		{
1147
			$changes['mtitle'] = '\'Upgrading the forum...\'';
1148
			$changes['mmessage'] = '\'Don\\\'t worry, we will be back shortly with an updated forum.  It will only be a minute ;).\'';
1149
		}
1150
	}
1151
1152
	if ($command_line)
1153
		echo ' * Updating Settings.php...';
1154
1155
	// Fix some old paths.
1156
	if (substr($boarddir, 0, 1) == '.')
1157
		$changes['boarddir'] = '\'' . fixRelativePath($boarddir) . '\'';
1158
1159
	if (substr($sourcedir, 0, 1) == '.')
1160
		$changes['sourcedir'] = '\'' . fixRelativePath($sourcedir) . '\'';
1161
1162
	if (empty($cachedir) || substr($cachedir, 0, 1) == '.')
1163
		$changes['cachedir'] = '\'' . fixRelativePath($boarddir) . '/cache\'';
1164
1165
	// If they have a "host:port" setup for the host, split that into separate values
1166
	// You should never have a : in the hostname if you're not on MySQL, but better safe than sorry
1167
	if (strpos($db_server, ':') !== false && $db_type == 'mysql')
1168
	{
1169
		list ($db_server, $db_port) = explode(':', $db_server);
1170
1171
		$changes['db_server'] = '\'' . $db_server . '\'';
1172
1173
		// Only set this if we're not using the default port
1174
		if ($db_port != ini_get('mysqli.default_port'))
1175
			$changes['db_port'] = (int) $db_port;
1176
	}
1177
	elseif (!empty($db_port))
1178
	{
1179
		// If db_port is set and is the same as the default, set it to ''
1180
		if ($db_type == 'mysql')
1181
		{
1182
			if ($db_port == ini_get('mysqli.default_port'))
1183
				$changes['db_port'] = '\'\'';
1184
			elseif ($db_type == 'postgresql' && $db_port == 5432)
1185
				$changes['db_port'] = '\'\'';
1186
		}
1187
	}
1188
1189
	// Maybe we haven't had this option yet?
1190
	if (empty($packagesdir))
1191
		$changes['packagesdir'] = '\'' . fixRelativePath($boarddir) . '/Packages\'';
1192
1193
	// Add support for $tasksdir var.
1194
	if (empty($tasksdir))
1195
		$changes['tasksdir'] = '\'' . fixRelativePath($sourcedir) . '/tasks\'';
1196
1197
	// Make sure we fix the language as well.
1198
	if (stristr($language, '-utf8'))
1199
		$changes['language'] = '\'' . str_ireplace('-utf8', '', $language) . '\'';
1200
1201
	// @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...
1202
1203
	// Update Settings.php with the new settings.
1204
	require_once($sourcedir . '/Subs-Admin.php');
1205
	updateSettingsFile($changes);
1206
1207
	if ($command_line)
1208
		echo ' Successful.' . "\n";
1209
1210
	// Are we doing debug?
1211
	if (isset($_POST['debug']))
1212
	{
1213
		$upcontext['upgrade_status']['debug'] = true;
1214
		$is_debug = true;
1215
	}
1216
1217
	// If we're not backing up then jump one.
1218
	if (empty($_POST['backup']))
1219
		$upcontext['current_step']++;
1220
1221
	// If we've got here then let's proceed to the next step!
1222
	return true;
1223
}
1224
1225
// Backup the database - why not...
1226
function BackupDatabase()
1227
{
1228
	global $upcontext, $db_prefix, $command_line, $support_js, $file_steps, $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...
1229
1230
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1231
	$upcontext['page_title'] = 'Backup Database';
1232
1233
	// Done it already - js wise?
1234
	if (!empty($_POST['backup_done']))
1235
		return true;
1236
1237
	// Some useful stuff here.
1238
	db_extend();
1239
1240
	// Might need this as well
1241
	db_extend('packages');
1242
1243
	// Get all the table names.
1244
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1245
	$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...
1246
	$tables = $smcFunc['db_list_tables']($db, $filter);
1247
1248
	$table_names = array();
1249
	foreach ($tables as $table)
1250
		if (substr($table, 0, 7) !== 'backup_')
1251
			$table_names[] = $table;
1252
1253
	$upcontext['table_count'] = count($table_names);
1254
	$upcontext['cur_table_num'] = $_GET['substep'];
1255
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1256
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1257
	// For non-java auto submit...
1258
	$file_steps = $upcontext['table_count'];
1259
1260
	// What ones have we already done?
1261
	foreach ($table_names as $id => $table)
1262
		if ($id < $_GET['substep'])
1263
			$upcontext['previous_tables'][] = $table;
1264
1265
	if ($command_line)
1266
		echo 'Backing Up Tables.';
1267
1268
	// If we don't support javascript we backup here.
1269
	if (!$support_js || isset($_GET['xml']))
1270
	{
1271
		// Backup each table!
1272
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1273
		{
1274
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1275
			$upcontext['cur_table_num'] = $substep + 1;
1276
1277
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1278
1279
			// Do we need to pause?
1280
			nextSubstep($substep);
1281
1282
			backupTable($table_names[$substep]);
1283
1284
			// If this is XML to keep it nice for the user do one table at a time anyway!
1285
			if (isset($_GET['xml']))
1286
				return upgradeExit();
1287
		}
1288
1289
		if ($command_line)
1290
		{
1291
			echo "\n" . ' Successful.\'' . "\n";
1292
			flush();
1293
		}
1294
		$upcontext['step_progress'] = 100;
1295
1296
		$_GET['substep'] = 0;
1297
		// Make sure we move on!
1298
		return true;
1299
	}
1300
1301
	// Either way next place to post will be database changes!
1302
	$_GET['substep'] = 0;
1303
	return false;
1304
}
1305
1306
// Backup one table...
1307
function backupTable($table)
1308
{
1309
	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...
1310
1311
	if ($command_line)
1312
	{
1313
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1314
		flush();
1315
	}
1316
1317
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1318
1319
	if ($command_line)
1320
		echo ' done.';
1321
}
1322
1323
// Step 2: Everything.
1324
function DatabaseChanges()
1325
{
1326
	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...
1327
	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...
1328
1329
	// Have we just completed this?
1330
	if (!empty($_POST['database_done']))
1331
		return true;
1332
1333
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1334
	$upcontext['page_title'] = 'Database Changes';
1335
1336
	// All possible files.
1337
	// Name, < version, insert_on_complete
1338
	$files = array(
1339
		array('upgrade_1-0.sql', '1.1', '1.1 RC0'),
1340
		array('upgrade_1-1.sql', '2.0', '2.0 a'),
1341
		array('upgrade_2-0_' . $db_type . '.sql', '2.1', '2.1 dev0'),
1342
		array('upgrade_2-1_' . $db_type . '.sql', '3.0', SMF_VERSION),
1343
	);
1344
1345
	// How many files are there in total?
1346
	if (isset($_GET['filecount']))
1347
		$upcontext['file_count'] = (int) $_GET['filecount'];
1348
	else
1349
	{
1350
		$upcontext['file_count'] = 0;
1351
		foreach ($files as $file)
1352
		{
1353
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1354
				$upcontext['file_count']++;
1355
		}
1356
	}
1357
1358
	// Do each file!
1359
	$did_not_do = count($files) - $upcontext['file_count'];
1360
	$upcontext['step_progress'] = 0;
1361
	$upcontext['cur_file_num'] = 0;
1362
	foreach ($files as $file)
1363
	{
1364
		if ($did_not_do)
1365
			$did_not_do--;
1366
		else
1367
		{
1368
			$upcontext['cur_file_num']++;
1369
			$upcontext['cur_file_name'] = $file[0];
1370
			// Do we actually need to do this still?
1371
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1372
			{
1373
				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1374
				if ($nextFile)
1375
				{
1376
					// Only update the version of this if complete.
1377
					$smcFunc['db_insert']('replace',
1378
						$db_prefix . 'settings',
1379
						array('variable' => 'string', 'value' => 'string'),
1380
						array('smfVersion', $file[2]),
1381
						array('variable')
1382
					);
1383
1384
					$modSettings['smfVersion'] = $file[2];
1385
				}
1386
1387
				// If this is XML we only do this stuff once.
1388
				if (isset($_GET['xml']))
1389
				{
1390
					// Flag to move on to the next.
1391
					$upcontext['completed_step'] = true;
1392
					// Did we complete the whole file?
1393
					if ($nextFile)
1394
						$upcontext['current_debug_item_num'] = -1;
1395
					return upgradeExit();
1396
				}
1397
				elseif ($support_js)
1398
					break;
1399
			}
1400
			// Set the progress bar to be right as if we had - even if we hadn't...
1401
			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1402
		}
1403
	}
1404
1405
	$_GET['substep'] = 0;
1406
	// So the template knows we're done.
1407
	if (!$support_js)
1408
	{
1409
		$upcontext['changes_complete'] = true;
1410
1411
		return true;
1412
	}
1413
	return false;
1414
}
1415
1416
1417
// Delete the damn thing!
1418
function DeleteUpgrade()
1419
{
1420
	global $command_line, $language, $upcontext, $sourcedir, $forum_version, $user_info, $maintenance, $smcFunc, $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...
1421
1422
	// Now it's nice to have some of the basic SMF source files.
1423
	if (!isset($_GET['ssi']) && !$command_line)
1424
		redirectLocation('&ssi=1');
1425
1426
	$upcontext['sub_template'] = 'upgrade_complete';
1427
	$upcontext['page_title'] = 'Upgrade Complete';
1428
1429
	$endl = $command_line ? "\n" : '<br>' . "\n";
1430
1431
	$changes = array(
1432
		'language' => '\'' . (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language) . '\'',
1433
		'db_error_send' => '1',
1434
		'upgradeData' => '\'\'',
1435
	);
1436
1437
	// Are we in maintenance mode?
1438
	if (isset($upcontext['user']['main']))
1439
	{
1440
		if ($command_line)
1441
			echo ' * ';
1442
		$upcontext['removed_maintenance'] = true;
1443
		$changes['maintenance'] = $upcontext['user']['main'];
1444
	}
1445
	// Otherwise if somehow we are in 2 let's go to 1.
1446
	elseif (!empty($maintenance) && $maintenance == 2)
1447
		$changes['maintenance'] = 1;
1448
1449
	// Wipe this out...
1450
	$upcontext['user'] = array();
1451
1452
	require_once($sourcedir . '/Subs-Admin.php');
1453
	updateSettingsFile($changes);
1454
1455
	// Clean any old cache files away.
1456
	upgrade_clean_cache();
1457
1458
	// Can we delete the file?
1459
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1460
1461
	// Now is the perfect time to fetch the SM files.
1462
	if ($command_line)
1463
		cli_scheduled_fetchSMfiles();
1464
	else
1465
	{
1466
		require_once($sourcedir . '/ScheduledTasks.php');
1467
		$forum_version = SMF_VERSION; // The variable is usually defined in index.php so lets just use the constant to do it for us.
1468
		scheduled_fetchSMfiles(); // Now go get those files!
1469
	}
1470
1471
	// Log what we've done.
1472
	if (empty($user_info['id']))
1473
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
1474
1475
	// Log the action manually, so CLI still works.
1476
	$smcFunc['db_insert']('',
1477
		'{db_prefix}log_actions',
1478
		array(
1479
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'inet', 'action' => 'string',
1480
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
1481
		),
1482
		array(
1483
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
1484
			0, 0, 0, json_encode(array('version' => $forum_version, 'member' => $user_info['id'])),
1485
		),
1486
		array('id_action')
1487
	);
1488
	$user_info['id'] = 0;
1489
1490
	// Save the current database version.
1491
	$server_version = $smcFunc['db_server_info']();
1492
	if ($db_type == 'mysql' && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51')))
1493
		updateSettings(array('db_mysql_group_by_fix' => '1'));
1494
1495
	if ($command_line)
1496
	{
1497
		echo $endl;
1498
		echo 'Upgrade Complete!', $endl;
1499
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
1500
		exit;
1501
	}
1502
1503
	// Make sure it says we're done.
1504
	$upcontext['overall_percent'] = 100;
1505
	if (isset($upcontext['step_progress']))
1506
		unset($upcontext['step_progress']);
1507
1508
	$_GET['substep'] = 0;
1509
	return false;
1510
}
1511
1512
// Just like the built in one, but setup for CLI to not use themes.
1513
function cli_scheduled_fetchSMfiles()
1514
{
1515
	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...
1516
1517
	if (empty($modSettings['time_format']))
1518
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1519
1520
	// What files do we want to get
1521
	$request = $smcFunc['db_query']('', '
1522
		SELECT id_file, filename, path, parameters
1523
		FROM {db_prefix}admin_info_files',
1524
		array(
1525
		)
1526
	);
1527
1528
	$js_files = array();
1529
	while ($row = $smcFunc['db_fetch_assoc']($request))
1530
	{
1531
		$js_files[$row['id_file']] = array(
1532
			'filename' => $row['filename'],
1533
			'path' => $row['path'],
1534
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode($forum_version)),
1535
		);
1536
	}
1537
	$smcFunc['db_free_result']($request);
1538
1539
	// We're gonna need fetch_web_data() to pull this off.
1540
	require_once($sourcedir . '/Subs-Package.php');
1541
1542
	foreach ($js_files as $ID_FILE => $file)
1543
	{
1544
		// Create the url
1545
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'https://www.simplemachines.org' : '';
1546
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1547
1548
		// Get the file
1549
		$file_data = fetch_web_data($url);
1550
1551
		// If we got an error - give up - the site might be down.
1552
		if ($file_data === false)
1553
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
1554
1555
		// Save the file to the database.
1556
		$smcFunc['db_query']('substring', '
1557
			UPDATE {db_prefix}admin_info_files
1558
			SET data = SUBSTRING({string:file_data}, 1, 65534)
1559
			WHERE id_file = {int:id_file}',
1560
			array(
1561
				'id_file' => $ID_FILE,
1562
				'file_data' => $file_data,
1563
			)
1564
		);
1565
	}
1566
	return true;
1567
}
1568
1569
function convertSettingsToTheme()
1570
{
1571
	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...
1572
1573
	$values = array(
1574
		'show_latest_member' => @$GLOBALS['showlatestmember'],
1575
		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
1576
		'show_modify' => @$GLOBALS['showmodify'],
1577
		'show_user_images' => @$GLOBALS['showuserpic'],
1578
		'show_blurb' => @$GLOBALS['showusertext'],
1579
		'show_gender' => @$GLOBALS['showgenderimage'],
1580
		'show_newsfader' => @$GLOBALS['shownewsfader'],
1581
		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
1582
		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
1583
		'linktree_link' => @$GLOBALS['curposlinks'],
1584
		'show_profile_buttons' => @$GLOBALS['profilebutton'],
1585
		'show_mark_read' => @$GLOBALS['showmarkread'],
1586
		'newsfader_time' => @$GLOBALS['fadertime'],
1587
		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
1588
		'enable_news' => @$GLOBALS['enable_news'],
1589
		'return_to_post' => @$modSettings['returnToPost'],
1590
	);
1591
1592
	$themeData = array();
1593
	foreach ($values as $variable => $value)
1594
	{
1595
		if (!isset($value) || $value === null)
1596
			$value = 0;
1597
1598
		$themeData[] = array(0, 1, $variable, $value);
1599
	}
1600
	if (!empty($themeData))
1601
	{
1602
		$smcFunc['db_insert']('ignore',
1603
			$db_prefix . 'themes',
1604
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
1605
			$themeData,
1606
			array('id_member', 'id_theme', 'variable')
1607
		);
1608
	}
1609
}
1610
1611
// This function only works with MySQL but that's fine as it is only used for v1.0.
1612
function convertSettingstoOptions()
1613
{
1614
	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...
1615
1616
	// Format: new_setting -> old_setting_name.
1617
	$values = array(
1618
		'calendar_start_day' => 'cal_startmonday',
1619
		'view_newest_first' => 'viewNewestFirst',
1620
		'view_newest_pm_first' => 'viewNewestFirst',
1621
	);
1622
1623
	foreach ($values as $variable => $value)
1624
	{
1625
		if (empty($modSettings[$value[0]]))
1626
			continue;
1627
1628
		$smcFunc['db_query']('', '
1629
			INSERT IGNORE INTO {db_prefix}themes
1630
				(id_member, id_theme, variable, value)
1631
			SELECT id_member, 1, {string:variable}, {string:value}
1632
			FROM {db_prefix}members',
1633
			array(
1634
				'variable' => $variable,
1635
				'value' => $modSettings[$value[0]],
1636
				'db_error_skip' => true,
1637
			)
1638
		);
1639
1640
		$smcFunc['db_query']('', '
1641
			INSERT IGNORE INTO {db_prefix}themes
1642
				(id_member, id_theme, variable, value)
1643
			VALUES (-1, 1, {string:variable}, {string:value})',
1644
			array(
1645
				'variable' => $variable,
1646
				'value' => $modSettings[$value[0]],
1647
				'db_error_skip' => true,
1648
			)
1649
		);
1650
	}
1651
}
1652
1653
function php_version_check()
1654
{
1655
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
1656
}
1657
1658
function db_version_check()
1659
{
1660
	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...
1661
1662
	$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...
1663
	$curver = preg_replace('~\-.+?$~', '', $curver);
1664
1665
	return version_compare($databases[$db_type]['version'], $curver, '<=');
1666
}
1667
1668
function fixRelativePath($path)
1669
{
1670
	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...
1671
1672
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
1673
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
1674
}
1675
1676
function parse_sql($filename)
1677
{
1678
	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...
1679
	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...
1680
1681
/*
1682
	Failure allowed on:
1683
		- INSERT INTO but not INSERT IGNORE INTO.
1684
		- UPDATE IGNORE but not UPDATE.
1685
		- ALTER TABLE and ALTER IGNORE TABLE.
1686
		- DROP TABLE.
1687
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
1688
1689
	If a comment...
1690
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
1691
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
1692
		- is only ---#, it is "done." and then a break - only shown in debug.
1693
		- begins with ---{ it is a code block terminating at ---}.
1694
1695
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
1696
1697
	Replaces the following variables:
1698
		- {$boarddir}
1699
		- {$boardurl}
1700
		- {$db_prefix}
1701
		- {$db_collation}
1702
*/
1703
1704
	// May want to use extended functionality.
1705
	db_extend();
1706
	db_extend('packages');
1707
1708
	// Our custom error handler - does nothing but does stop public errors from XML!
1709
	set_error_handler(
1710
		function ($errno, $errstr, $errfile, $errline) use ($support_js)
1711
		{
1712
			if ($support_js)
1713
				return true;
1714
			else
1715
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
1716
		}
1717
	);
1718
1719
	// If we're on MySQL, set {db_collation}; this approach is used throughout upgrade_2-0_mysql.php to set new tables to utf8
1720
	// Note it is expected to be in the format: ENGINE=MyISAM{$db_collation};
1721
	if ($db_type == 'mysql')
1722
		$db_collation = ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1723
	else
1724
		$db_collation = '';
1725
1726
	$endl = $command_line ? "\n" : '<br>' . "\n";
1727
1728
	$lines = file($filename);
1729
1730
	$current_type = 'sql';
1731
	$current_data = '';
1732
	$substep = 0;
1733
	$last_step = '';
1734
1735
	// Make sure all newly created tables will have the proper characters set; this approach is used throughout upgrade_2-1_mysql.php
1736
	if (isset($db_character_set) && $db_character_set === 'utf8')
1737
		$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
1738
1739
	// Count the total number of steps within this file - for progress.
1740
	$file_steps = substr_count(implode('', $lines), '---#');
1741
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
1742
	$upcontext['debug_items'] = $file_steps;
1743
	$upcontext['current_item_num'] = 0;
1744
	$upcontext['current_item_name'] = '';
1745
	$upcontext['current_debug_item_num'] = 0;
1746
	$upcontext['current_debug_item_name'] = '';
1747
	// This array keeps a record of what we've done in case java is dead...
1748
	$upcontext['actioned_items'] = array();
1749
1750
	$done_something = false;
1751
1752
	foreach ($lines as $line_number => $line)
1753
	{
1754
		$do_current = $substep >= $_GET['substep'];
1755
1756
		// Get rid of any comments in the beginning of the line...
1757
		if (substr(trim($line), 0, 2) === '/*')
1758
			$line = preg_replace('~/\*.+?\*/~', '', $line);
1759
1760
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
1761
		if ($is_debug && !$support_js && $command_line)
1762
			flush();
1763
1764
		if (trim($line) === '')
1765
			continue;
1766
1767
		if (trim(substr($line, 0, 3)) === '---')
1768
		{
1769
			$type = substr($line, 3, 1);
1770
1771
			// An error??
1772
			if (trim($current_data) != '' && $type !== '}')
1773
			{
1774
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
1775
				if ($command_line)
1776
					echo $upcontext['error_message'];
1777
			}
1778
1779
			if ($type == ' ')
1780
			{
1781
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
1782
				{
1783
					echo ' Successful.', $endl;
1784
					flush();
1785
				}
1786
1787
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
1788
				$upcontext['current_item_num']++;
1789
				$upcontext['current_item_name'] = $last_step;
1790
1791
				if ($do_current)
1792
				{
1793
					$upcontext['actioned_items'][] = $last_step;
1794
					if ($command_line)
1795
						echo ' * ';
1796
				}
1797
			}
1798
			elseif ($type == '#')
1799
			{
1800
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
1801
1802
				$upcontext['current_debug_item_num']++;
1803
				if (trim($line) != '---#')
1804
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
1805
1806
				// Have we already done something?
1807
				if (isset($_GET['xml']) && $done_something)
1808
				{
1809
					restore_error_handler();
1810
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
1811
				}
1812
1813
				if ($do_current)
1814
				{
1815
					if (trim($line) == '---#' && $command_line)
1816
						echo ' done.', $endl;
1817
					elseif ($command_line)
1818
						echo ' +++ ', rtrim(substr($line, 4));
1819
					elseif (trim($line) != '---#')
1820
					{
1821
						if ($is_debug)
1822
							$upcontext['actioned_items'][] = htmlspecialchars(rtrim(substr($line, 4)));
1823
					}
1824
				}
1825
1826
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
1827
				{
1828
					if ($command_line)
1829
						echo ' * ';
1830
					else
1831
						$upcontext['actioned_items'][] = $last_step;
1832
				}
1833
1834
				// Small step - only if we're actually doing stuff.
1835
				if ($do_current)
1836
					nextSubstep(++$substep);
1837
				else
1838
					$substep++;
1839
			}
1840
			elseif ($type == '{')
1841
				$current_type = 'code';
1842
			elseif ($type == '}')
1843
			{
1844
				$current_type = 'sql';
1845
1846
				if (!$do_current)
1847
				{
1848
					$current_data = '';
1849
					continue;
1850
				}
1851
1852
				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...
1853
				{
1854
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
1855
					if ($command_line)
1856
						echo $upcontext['error_message'];
1857
				}
1858
1859
				// Done with code!
1860
				$current_data = '';
1861
				$done_something = true;
1862
			}
1863
1864
			continue;
1865
		}
1866
1867
		$current_data .= $line;
1868
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
1869
		{
1870
			if ((!$support_js || isset($_GET['xml'])))
1871
			{
1872
				if (!$do_current)
1873
				{
1874
					$current_data = '';
1875
					continue;
1876
				}
1877
1878
				$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));
1879
1880
				upgrade_query($current_data);
1881
1882
				// @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...
1883
				/*
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...
1884
				$result = $smcFunc['db_query']('', $current_data, false, false);
1885
				// Went wrong?
1886
				if (!$result)
1887
				{
1888
					// Bit of a bodge - do we want the error?
1889
					if (!empty($upcontext['return_error']))
1890
					{
1891
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
1892
						return false;
1893
					}
1894
				}*/
1895
				$done_something = true;
1896
			}
1897
			$current_data = '';
1898
		}
1899
		// If this is xml based and we're just getting the item name then that's grand.
1900
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
1901
		{
1902
			restore_error_handler();
1903
			return false;
1904
		}
1905
1906
		// Clean up by cleaning any step info.
1907
		$step_progress = array();
1908
		$custom_warning = '';
1909
	}
1910
1911
	// Put back the error handler.
1912
	restore_error_handler();
1913
1914
	if ($command_line)
1915
	{
1916
		echo ' Successful.' . "\n";
1917
		flush();
1918
	}
1919
1920
	$_GET['substep'] = 0;
1921
	return true;
1922
}
1923
1924
function upgrade_query($string, $unbuffered = false)
1925
{
1926
	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...
1927
	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...
1928
1929
	// Get the query result - working around some SMF specific security - just this once!
1930
	$modSettings['disableQueryCheck'] = true;
1931
	$db_unbuffered = $unbuffered;
1932
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
1933
	$db_unbuffered = false;
1934
1935
	// Failure?!
1936
	if ($result !== false)
1937
		return $result;
1938
1939
	$db_error_message = $smcFunc['db_error']($db_connection);
1940
	// If MySQL we do something more clever.
1941
	if ($db_type == 'mysql')
1942
	{
1943
		$mysqli_errno = mysqli_errno($db_connection);
1944
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR'));
1945
1946
		// Error numbers:
1947
		//    1016: Can't open file '....MYI'
1948
		//    1050: Table already exists.
1949
		//    1054: Unknown column name.
1950
		//    1060: Duplicate column name.
1951
		//    1061: Duplicate key name.
1952
		//    1062: Duplicate entry for unique key.
1953
		//    1068: Multiple primary keys.
1954
		//    1072: Key column '%s' doesn't exist in table.
1955
		//    1091: Can't drop key, doesn't exist.
1956
		//    1146: Table doesn't exist.
1957
		//    2013: Lost connection to server during query.
1958
1959
		if ($mysqli_errno == 1016)
1960
		{
1961
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
1962
			{
1963
				mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
1964
				$result = mysqli_query($db_connection, $string);
1965
				if ($result !== false)
1966
					return $result;
1967
			}
1968
		}
1969
		elseif ($mysqli_errno == 2013)
1970
		{
1971
			$db_connection = mysqli_connect($db_server, $db_user, $db_passwd);
1972
			mysqli_select_db($db_connection, $db_name);
1973
			if ($db_connection)
1974
			{
1975
				$result = mysqli_query($db_connection, $string);
1976
				if ($result !== false)
1977
					return $result;
1978
			}
1979
		}
1980
		// Duplicate column name... should be okay ;).
1981
		elseif (in_array($mysqli_errno, array(1060, 1061, 1068, 1091)))
1982
			return false;
1983
		// Duplicate insert... make sure it's the proper type of query ;).
1984
		elseif (in_array($mysqli_errno, array(1054, 1062, 1146)) && $error_query)
1985
			return false;
1986
		// Creating an index on a non-existent column.
1987
		elseif ($mysqli_errno == 1072)
1988
			return false;
1989
		elseif ($mysqli_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
1990
			return false;
1991
	}
1992
	// If a table already exists don't go potty.
1993
	else
1994
	{
1995
		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U')))
1996
		{
1997
			if (strpos($db_error_message, 'exist') !== false)
1998
				return true;
1999
		}
2000
		elseif (strpos(trim($string), 'INSERT ') !== false)
2001
		{
2002
			if (strpos($db_error_message, 'duplicate') !== false)
2003
				return true;
2004
		}
2005
	}
2006
2007
	// Get the query string so we pass everything.
2008
	$query_string = '';
2009
	foreach ($_GET as $k => $v)
2010
		$query_string .= ';' . $k . '=' . $v;
2011
	if (strlen($query_string) != 0)
2012
		$query_string = '?' . substr($query_string, 1);
2013
2014
	if ($command_line)
2015
	{
2016
		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
2017
		die;
2018
	}
2019
2020
	// Bit of a bodge - do we want the error?
2021
	if (!empty($upcontext['return_error']))
2022
	{
2023
		$upcontext['error_message'] = $db_error_message;
2024
		$upcontext['error_string'] = $string;
2025
		return false;
2026
	}
2027
2028
	// Otherwise we have to display this somewhere appropriate if possible.
2029
	$upcontext['forced_error_message'] = '
2030
			<strong>Unsuccessful!</strong><br>
2031
2032
			<div style="margin: 2ex;">
2033
				This query:
2034
				<blockquote><pre>' . nl2br(htmlspecialchars(trim($string))) . ';</pre></blockquote>
2035
2036
				Caused the error:
2037
				<blockquote>' . nl2br(htmlspecialchars($db_error_message)) . '</blockquote>
2038
			</div>
2039
2040
			<form action="' . $upgradeurl . $query_string . '" method="post">
2041
				<input type="submit" value="Try again" class="button">
2042
			</form>
2043
		</div>';
2044
2045
	upgradeExit();
2046
}
2047
2048
// This performs a table alter, but does it unbuffered so the script can time out professionally.
2049
function protected_alter($change, $substep, $is_test = false)
2050
{
2051
	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...
2052
2053
	db_extend('packages');
2054
2055
	// Firstly, check whether the current index/column exists.
2056
	$found = false;
2057
	if ($change['type'] === 'column')
2058
	{
2059
		$columns = $smcFunc['db_list_columns']('{db_prefix}' . $change['table'], true);
2060
		foreach ($columns as $column)
2061
		{
2062
			// Found it?
2063
			if ($column['name'] === $change['name'])
2064
			{
2065
				$found |= 1;
2066
				// Do some checks on the data if we have it set.
2067
				if (isset($change['col_type']))
2068
					$found &= $change['col_type'] === $column['type'];
2069
				if (isset($change['null_allowed']))
2070
					$found &= $column['null'] == $change['null_allowed'];
2071
				if (isset($change['default']))
2072
					$found &= $change['default'] === $column['default'];
2073
			}
2074
		}
2075
	}
2076
	elseif ($change['type'] === 'index')
2077
	{
2078
		$request = upgrade_query('
2079
			SHOW INDEX
2080
			FROM ' . $db_prefix . $change['table']);
2081
		if ($request !== false)
2082
		{
2083
			$cur_index = array();
2084
2085
			while ($row = $smcFunc['db_fetch_assoc']($request))
2086
				if ($row['Key_name'] === $change['name'])
2087
					$cur_index[(int) $row['Seq_in_index']] = $row['Column_name'];
2088
2089
			ksort($cur_index, SORT_NUMERIC);
2090
			$found = array_values($cur_index) === $change['target_columns'];
2091
2092
			$smcFunc['db_free_result']($request);
2093
		}
2094
	}
2095
2096
	// If we're trying to add and it's added, we're done.
2097
	if ($found && in_array($change['method'], array('add', 'change')))
2098
		return true;
2099
	// Otherwise if we're removing and it wasn't found we're also done.
2100
	elseif (!$found && in_array($change['method'], array('remove', 'change_remove')))
2101
		return true;
2102
	// Otherwise is it just a test?
2103
	elseif ($is_test)
2104
		return false;
2105
2106
	// Not found it yet? Bummer! How about we see if we're currently doing it?
2107
	$running = false;
2108
	$found = false;
2109
	while (1 == 1)
2110
	{
2111
		$request = upgrade_query('
2112
			SHOW FULL PROCESSLIST');
2113
		while ($row = $smcFunc['db_fetch_assoc']($request))
2114
		{
2115
			if (strpos($row['Info'], 'ALTER TABLE ' . $db_prefix . $change['table']) !== false && strpos($row['Info'], $change['text']) !== false)
2116
				$found = true;
2117
		}
2118
2119
		// Can't find it? Then we need to run it fools!
2120
		if (!$found && !$running)
2121
		{
2122
			$smcFunc['db_free_result']($request);
2123
2124
			$success = upgrade_query('
2125
				ALTER TABLE ' . $db_prefix . $change['table'] . '
2126
				' . $change['text'], true) !== false;
2127
2128
			if (!$success)
2129
				return false;
2130
2131
			// Return
2132
			$running = true;
2133
		}
2134
		// What if we've not found it, but we'd ran it already? Must of completed.
2135
		elseif (!$found)
2136
		{
2137
			$smcFunc['db_free_result']($request);
2138
			return true;
2139
		}
2140
2141
		// Pause execution for a sec or three.
2142
		sleep(3);
2143
2144
		// Can never be too well protected.
2145
		nextSubstep($substep);
2146
	}
2147
2148
	// Protect it.
2149
	nextSubstep($substep);
2150
}
2151
2152
/**
2153
 * Alter a text column definition preserving its character set.
2154
 *
2155
 * @param array $change
2156
 * @param int $substep
2157
 */
2158
function textfield_alter($change, $substep)
2159
{
2160
	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...
2161
2162
	$request = $smcFunc['db_query']('', '
2163
		SHOW FULL COLUMNS
2164
		FROM {db_prefix}' . $change['table'] . '
2165
		LIKE {string:column}',
2166
		array(
2167
			'column' => $change['column'],
2168
			'db_error_skip' => true,
2169
		)
2170
	);
2171
	if ($smcFunc['db_num_rows']($request) === 0)
2172
		die('Unable to find column ' . $change['column'] . ' inside table ' . $db_prefix . $change['table']);
2173
	$table_row = $smcFunc['db_fetch_assoc']($request);
2174
	$smcFunc['db_free_result']($request);
2175
2176
	// If something of the current column definition is different, fix it.
2177
	$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']);
2178
2179
	// Columns that previously allowed null, need to be converted first.
2180
	$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
2181
2182
	// Get the character set that goes with the collation of the column.
2183
	if ($column_fix && !empty($table_row['Collation']))
2184
	{
2185
		$request = $smcFunc['db_query']('', '
2186
			SHOW COLLATION
2187
			LIKE {string:collation}',
2188
			array(
2189
				'collation' => $table_row['Collation'],
2190
				'db_error_skip' => true,
2191
			)
2192
		);
2193
		// No results? Just forget it all together.
2194
		if ($smcFunc['db_num_rows']($request) === 0)
2195
			unset($table_row['Collation']);
2196
		else
2197
			$collation_info = $smcFunc['db_fetch_assoc']($request);
2198
		$smcFunc['db_free_result']($request);
2199
	}
2200
2201
	if ($column_fix)
2202
	{
2203
		// Make sure there are no NULL's left.
2204
		if ($null_fix)
2205
			$smcFunc['db_query']('', '
2206
				UPDATE {db_prefix}' . $change['table'] . '
2207
				SET ' . $change['column'] . ' = {string:default}
2208
				WHERE ' . $change['column'] . ' IS NULL',
2209
				array(
2210
					'default' => isset($change['default']) ? $change['default'] : '',
2211
					'db_error_skip' => true,
2212
				)
2213
			);
2214
2215
		// Do the actual alteration.
2216
		$smcFunc['db_query']('', '
2217
			ALTER TABLE {db_prefix}' . $change['table'] . '
2218
			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}' : ''),
2219
			array(
2220
				'default' => isset($change['default']) ? $change['default'] : '',
2221
				'db_error_skip' => true,
2222
			)
2223
		);
2224
	}
2225
	nextSubstep($substep);
2226
}
2227
2228
// Check if we need to alter this query.
2229
function checkChange(&$change)
2230
{
2231
	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...
2232
	static $database_version, $where_field_support;
2233
2234
	// Attempt to find a database_version.
2235
	if (empty($database_version))
2236
	{
2237
		$database_version = $databases[$db_type]['version_check'];
2238
		$where_field_support = $db_type == 'mysql' && version_compare('5.0', $database_version, '<=');
2239
	}
2240
2241
	// Not a column we need to check on?
2242
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
2243
		return;
2244
2245
	// Break it up you (six|seven).
2246
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
2247
2248
	// Can we support a shortcut method?
2249
	if ($where_field_support)
2250
	{
2251
		// Get the details about this change.
2252
		$request = $smcFunc['db_query']('', '
2253
			SHOW FIELDS
2254
			FROM {db_prefix}{raw:table}
2255
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
2256
			array(
2257
				'table' => $change['table'],
2258
				'old_name' => $temp[1],
2259
				'new_name' => $temp[2],
2260
		));
2261
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2262
		if ($smcFunc['db_num_rows'] != 1)
2263
			return;
2264
2265
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
2266
		$smcFunc['db_free_result']($request);
2267
	}
2268
	else
2269
	{
2270
		// Do this the old fashion, sure method way.
2271
		$request = $smcFunc['db_query']('', '
2272
			SHOW FIELDS
2273
			FROM {db_prefix}{raw:table}',
2274
			array(
2275
				'table' => $change['table'],
2276
		));
2277
		// Mayday!
2278
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2279
		if ($smcFunc['db_num_rows'] == 0)
2280
			return;
2281
2282
		// Oh where, oh where has my little field gone. Oh where can it be...
2283
		while ($row = $smcFunc['db_query']($request))
2284
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
2285
			{
2286
				$current_type = $row['Type'];
2287
				break;
2288
			}
2289
	}
2290
2291
	// If this doesn't match, the column may of been altered for a reason.
2292
	if (trim($current_type) != trim($temp[3]))
2293
		$temp[3] = $current_type;
2294
2295
	// Piece this back together.
2296
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
2297
}
2298
2299
// The next substep.
2300
function nextSubstep($substep)
2301
{
2302
	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...
2303
	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...
2304
2305
	if ($_GET['substep'] < $substep)
2306
		$_GET['substep'] = $substep;
2307
2308
	if ($command_line)
2309
	{
2310
		if (time() - $start_time > 1 && empty($is_debug))
2311
		{
2312
			echo '.';
2313
			$start_time = time();
2314
		}
2315
		return;
2316
	}
2317
2318
	@set_time_limit(300);
2319
	if (function_exists('apache_reset_timeout'))
2320
		@apache_reset_timeout();
2321
2322
	if (time() - $start_time <= $timeLimitThreshold)
2323
		return;
2324
2325
	// Do we have some custom step progress stuff?
2326
	if (!empty($step_progress))
2327
	{
2328
		$upcontext['substep_progress'] = 0;
2329
		$upcontext['substep_progress_name'] = $step_progress['name'];
2330
		if ($step_progress['current'] > $step_progress['total'])
2331
			$upcontext['substep_progress'] = 99.9;
2332
		else
2333
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2334
2335
		// Make it nicely rounded.
2336
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2337
	}
2338
2339
	// If this is XML we just exit right away!
2340
	if (isset($_GET['xml']))
2341
		return upgradeExit();
2342
2343
	// We're going to pause after this!
2344
	$upcontext['pause'] = true;
2345
2346
	$upcontext['query_string'] = '';
2347
	foreach ($_GET as $k => $v)
2348
	{
2349
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2350
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2351
	}
2352
2353
	// Custom warning?
2354
	if (!empty($custom_warning))
2355
		$upcontext['custom_warning'] = $custom_warning;
2356
2357
	upgradeExit();
2358
}
2359
2360
function cmdStep0()
2361
{
2362
	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...
2363
	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...
2364
	$start_time = time();
2365
2366
	ob_end_clean();
2367
	ob_implicit_flush(true);
2368
	@set_time_limit(600);
2369
2370
	if (!isset($_SERVER['argv']))
2371
		$_SERVER['argv'] = array();
2372
	$_GET['maint'] = 1;
2373
2374
	foreach ($_SERVER['argv'] as $i => $arg)
2375
	{
2376
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2377
			$_GET['lang'] = $match[1];
2378
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2379
			continue;
2380
		elseif ($arg == '--no-maintenance')
2381
			$_GET['maint'] = 0;
2382
		elseif ($arg == '--debug')
2383
			$is_debug = true;
2384
		elseif ($arg == '--backup')
2385
			$_POST['backup'] = 1;
2386
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2387
			$_GET['conv'] = 1;
2388
		elseif ($i != 0)
2389
		{
2390
			echo 'SMF Command-line Upgrader
2391
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2392
2393
    --language=LANG         Reset the forum\'s language to LANG.
2394
    --no-maintenance        Don\'t put the forum into maintenance mode.
2395
    --debug                 Output debugging information.
2396
    --backup                Create backups of tables with "backup_" prefix.';
2397
			echo "\n";
2398
			exit;
2399
		}
2400
	}
2401
2402
	if (!php_version_check())
2403
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
2404
	if (!db_version_check())
2405
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2406
2407
	// Do some checks to make sure they have proper privileges
2408
	db_extend('packages');
2409
2410
	// CREATE
2411
	$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');
2412
2413
	// ALTER
2414
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'tinytext', 'null' => false, 'default' => ''));
2415
2416
	// DROP
2417
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2418
2419
	// Sorry... we need CREATE, ALTER and DROP
2420
	if (!$create || !$alter || !$drop)
2421
		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...
2422
2423
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2424
		&& @file_exists($sourcedir . '/QueryString.php')
2425
		&& @file_exists($sourcedir . '/ManageBoards.php');
2426
	if (!$check && !isset($modSettings['smfVersion']))
2427
		print_error('Error: Some files are missing or out-of-date.', true);
2428
2429
	// Do a quick version spot check.
2430
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
2431
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2432
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2433
		print_error('Error: Some files have not yet been updated properly.');
2434
2435
	// Make sure Settings.php is writable.
2436
	quickFileWritable($boarddir . '/Settings.php');
2437
	if (!is_writable($boarddir . '/Settings.php'))
2438
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2439
2440
	// Make sure Settings_bak.php is writable.
2441
	quickFileWritable($boarddir . '/Settings_bak.php');
2442
	if (!is_writable($boarddir . '/Settings_bak.php'))
2443
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2444
2445
	// Make sure db_last_error.php is writable.
2446
	quickFileWritable($boarddir . '/db_last_error.php');
2447
	if (!is_writable($boarddir . '/db_last_error.php'))
2448
		print_error('Error: Unable to obtain write access to "db_last_error.php".');
2449
2450
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
2451
		print_error('Error: Unable to obtain write access to "agreement.txt".');
2452
	elseif (isset($modSettings['agreement']))
2453
	{
2454
		$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...
2455
		fwrite($fp, $modSettings['agreement']);
2456
		fclose($fp);
2457
	}
2458
2459
	// Make sure Themes is writable.
2460
	quickFileWritable($modSettings['theme_dir']);
2461
2462
	if (!is_writable($modSettings['theme_dir']) && !isset($modSettings['smfVersion']))
2463
		print_error('Error: Unable to obtain write access to "Themes".');
2464
2465
	// Make sure cache directory exists and is writable!
2466
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
2467
	if (!file_exists($cachedir_temp))
2468
		@mkdir($cachedir_temp);
2469
2470
	// Make sure the cache temp dir is writable.
2471
	quickFileWritable($cachedir_temp);
2472
2473
	if (!is_writable($cachedir_temp))
2474
		print_error('Error: Unable to obtain write access to "cache".', true);
2475
2476
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
2477
		print_error('Error: Unable to find language files!', true);
2478
	else
2479
	{
2480
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
2481
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
2482
2483
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
2484
			print_error('Error: Language files out of date.', true);
2485
		if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
2486
			print_error('Error: Install language is missing for selected language.', true);
2487
2488
		// Otherwise include it!
2489
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
2490
	}
2491
2492
	// Make sure we skip the HTML for login.
2493
	$_POST['upcont'] = true;
2494
	$upcontext['current_step'] = 1;
2495
}
2496
2497
/**
2498
 * Handles converting your database to UTF-8
2499
 */
2500
function ConvertUtf8()
2501
{
2502
	global $upcontext, $db_character_set, $sourcedir, $smcFunc, $modSettings, $language, $db_prefix, $db_type, $command_line, $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...
2503
2504
	// Done it already?
2505
	if (!empty($_POST['utf8_done']))
2506
		return true;
2507
2508
	// First make sure they aren't already on UTF-8 before we go anywhere...
2509
	if ($db_type == 'postgresql' || ($db_character_set === 'utf8' && !empty($modSettings['global_character_set']) && $modSettings['global_character_set'] === 'UTF-8'))
2510
	{
2511
		$smcFunc['db_insert']('replace',
2512
			'{db_prefix}settings',
2513
			array('variable' => 'string', 'value' => 'string'),
2514
			array(array('global_character_set', 'UTF-8')),
2515
			array('variable')
2516
		);
2517
2518
		return true;
2519
	}
2520
	else
2521
	{
2522
		$upcontext['page_title'] = 'Converting to UTF8';
2523
		$upcontext['sub_template'] = isset($_GET['xml']) ? 'convert_xml' : 'convert_utf8';
2524
2525
		// The character sets used in SMF's language files with their db equivalent.
2526
		$charsets = array(
2527
			// Armenian
2528
			'armscii8' => 'armscii8',
2529
			// Chinese-traditional.
2530
			'big5' => 'big5',
2531
			// Chinese-simplified.
2532
			'gbk' => 'gbk',
2533
			// West European.
2534
			'ISO-8859-1' => 'latin1',
2535
			// Romanian.
2536
			'ISO-8859-2' => 'latin2',
2537
			// Turkish.
2538
			'ISO-8859-9' => 'latin5',
2539
			// Latvian
2540
			'ISO-8859-13' => 'latin7',
2541
			// West European with Euro sign.
2542
			'ISO-8859-15' => 'latin9',
2543
			// Thai.
2544
			'tis-620' => 'tis620',
2545
			// Persian, Chinese, etc.
2546
			'UTF-8' => 'utf8',
2547
			// Russian.
2548
			'windows-1251' => 'cp1251',
2549
			// Greek.
2550
			'windows-1253' => 'utf8',
2551
			// Hebrew.
2552
			'windows-1255' => 'utf8',
2553
			// Arabic.
2554
			'windows-1256' => 'cp1256',
2555
		);
2556
2557
		// Get a list of character sets supported by your MySQL server.
2558
		$request = $smcFunc['db_query']('', '
2559
			SHOW CHARACTER SET',
2560
			array(
2561
			)
2562
		);
2563
		$db_charsets = array();
2564
		while ($row = $smcFunc['db_fetch_assoc']($request))
2565
			$db_charsets[] = $row['Charset'];
2566
2567
		$smcFunc['db_free_result']($request);
2568
2569
		// Character sets supported by both MySQL and SMF's language files.
2570
		$charsets = array_intersect($charsets, $db_charsets);
2571
2572
		// Use the messages.body column as indicator for the database charset.
2573
		$request = $smcFunc['db_query']('', '
2574
			SHOW FULL COLUMNS
2575
			FROM {db_prefix}messages
2576
			LIKE {string:body_like}',
2577
			array(
2578
				'body_like' => 'body',
2579
			)
2580
		);
2581
		$column_info = $smcFunc['db_fetch_assoc']($request);
2582
		$smcFunc['db_free_result']($request);
2583
2584
		// A collation looks like latin1_swedish. We only need the character set.
2585
		list($upcontext['database_charset']) = explode('_', $column_info['Collation']);
2586
		$upcontext['database_charset'] = in_array($upcontext['database_charset'], $charsets) ? array_search($upcontext['database_charset'], $charsets) : $upcontext['database_charset'];
2587
2588
		// Detect whether a fulltext index is set.
2589
		$request = $smcFunc['db_query']('', '
2590
 			SHOW INDEX
2591
	  	    FROM {db_prefix}messages',
2592
			array(
2593
			)
2594
		);
2595
2596
		$upcontext['dropping_index'] = false;
2597
2598
		// If there's a fulltext index, we need to drop it first...
2599
		if ($request !== false || $smcFunc['db_num_rows']($request) != 0)
2600
		{
2601
			while ($row = $smcFunc['db_fetch_assoc']($request))
2602
				if ($row['Column_name'] == 'body' && (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT' || isset($row['Comment']) && $row['Comment'] == 'FULLTEXT'))
2603
					$upcontext['fulltext_index'][] = $row['Key_name'];
2604
			$smcFunc['db_free_result']($request);
2605
2606
			if (isset($upcontext['fulltext_index']))
2607
				$upcontext['fulltext_index'] = array_unique($upcontext['fulltext_index']);
2608
		}
2609
2610
		// Drop it and make a note...
2611
		if (!empty($upcontext['fulltext_index']))
2612
		{
2613
			$upcontext['dropping_index'] = true;
2614
2615
			$smcFunc['db_query']('', '
2616
  			ALTER TABLE {db_prefix}messages
2617
	  		DROP INDEX ' . implode(',
2618
		  	DROP INDEX ', $upcontext['fulltext_index']),
2619
				array(
2620
					'db_error_skip' => true,
2621
				)
2622
			);
2623
2624
			// Update the settings table
2625
			$smcFunc['db_insert']('replace',
2626
				'{db_prefix}settings',
2627
				array('variable' => 'string', 'value' => 'string'),
2628
				array('db_search_index', ''),
2629
				array('variable')
2630
			);
2631
		}
2632
2633
		// Figure out what charset we should be converting from...
2634
		$lang_charsets = array(
2635
			'arabic' => 'windows-1256',
2636
			'armenian_east' => 'armscii-8',
2637
			'armenian_west' => 'armscii-8',
2638
			'azerbaijani_latin' => 'ISO-8859-9',
2639
			'bangla' => 'UTF-8',
2640
			'belarusian' => 'ISO-8859-5',
2641
			'bulgarian' => 'windows-1251',
2642
			'cambodian' => 'UTF-8',
2643
			'chinese_simplified' => 'gbk',
2644
			'chinese_traditional' => 'big5',
2645
			'croation' => 'ISO-8859-2',
2646
			'czech' => 'ISO-8859-2',
2647
			'czech_informal' => 'ISO-8859-2',
2648
			'english_pirate' => 'UTF-8',
2649
			'esperanto' => 'ISO-8859-3',
2650
			'estonian' => 'ISO-8859-15',
2651
			'filipino_tagalog' => 'UTF-8',
2652
			'filipino_vasayan' => 'UTF-8',
2653
			'georgian' => 'UTF-8',
2654
			'greek' => 'ISO-8859-3',
2655
			'hebrew' => 'windows-1255',
2656
			'hungarian' => 'ISO-8859-2',
2657
			'irish' => 'UTF-8',
2658
			'japanese' => 'UTF-8',
2659
			'khmer' => 'UTF-8',
2660
			'korean' => 'UTF-8',
2661
			'kurdish_kurmanji' => 'ISO-8859-9',
2662
			'kurdish_sorani' => 'windows-1256',
2663
			'lao' => 'tis-620',
2664
			'latvian' => 'ISO-8859-13',
2665
			'lithuanian' => 'ISO-8859-4',
2666
			'macedonian' => 'UTF-8',
2667
			'malayalam' => 'UTF-8',
2668
			'mongolian' => 'UTF-8',
2669
			'nepali' => 'UTF-8',
2670
			'persian' => 'UTF-8',
2671
			'polish' => 'ISO-8859-2',
2672
			'romanian' => 'ISO-8859-2',
2673
			'russian' => 'windows-1252',
2674
			'sakha' => 'UTF-8',
2675
			'serbian_cyrillic' => 'ISO-8859-5',
2676
			'serbian_latin' => 'ISO-8859-2',
2677
			'sinhala' => 'UTF-8',
2678
			'slovak' => 'ISO-8859-2',
2679
			'slovenian' => 'ISO-8859-2',
2680
			'telugu' => 'UTF-8',
2681
			'thai' => 'tis-620',
2682
			'turkish' => 'ISO-8859-9',
2683
			'turkmen' => 'ISO-8859-9',
2684
			'ukranian' => 'windows-1251',
2685
			'urdu' => 'UTF-8',
2686
			'uzbek_cyrillic' => 'ISO-8859-5',
2687
			'uzbek_latin' => 'ISO-8859-5',
2688
			'vietnamese' => 'UTF-8',
2689
			'yoruba' => 'UTF-8'
2690
		);
2691
2692
		// Default to ISO-8859-1 unless we detected another supported charset
2693
		$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';
2694
2695
		$upcontext['charset_list'] = array_keys($charsets);
2696
2697
		// Translation table for the character sets not native for MySQL.
2698
		$translation_tables = array(
2699
			'windows-1255' => array(
2700
				'0x81' => '\'\'',		'0x8A' => '\'\'',		'0x8C' => '\'\'',
2701
				'0x8D' => '\'\'',		'0x8E' => '\'\'',		'0x8F' => '\'\'',
2702
				'0x90' => '\'\'',		'0x9A' => '\'\'',		'0x9C' => '\'\'',
2703
				'0x9D' => '\'\'',		'0x9E' => '\'\'',		'0x9F' => '\'\'',
2704
				'0xCA' => '\'\'',		'0xD9' => '\'\'',		'0xDA' => '\'\'',
2705
				'0xDB' => '\'\'',		'0xDC' => '\'\'',		'0xDD' => '\'\'',
2706
				'0xDE' => '\'\'',		'0xDF' => '\'\'',		'0xFB' => '0xD792',
2707
				'0xFC' => '0xE282AC',		'0xFF' => '0xD6B2',		'0xC2' => '0xFF',
2708
				'0x80' => '0xFC',		'0xE2' => '0xFB',		'0xA0' => '0xC2A0',
2709
				'0xA1' => '0xC2A1',		'0xA2' => '0xC2A2',		'0xA3' => '0xC2A3',
2710
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2711
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2712
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2713
				'0xAF' => '0xC2AF',		'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',
2714
				'0xB2' => '0xC2B2',		'0xB3' => '0xC2B3',		'0xB4' => '0xC2B4',
2715
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2716
				'0xB8' => '0xC2B8',		'0xB9' => '0xC2B9',		'0xBB' => '0xC2BB',
2717
				'0xBC' => '0xC2BC',		'0xBD' => '0xC2BD',		'0xBE' => '0xC2BE',
2718
				'0xBF' => '0xC2BF',		'0xD7' => '0xD7B3',		'0xD1' => '0xD781',
2719
				'0xD4' => '0xD7B0',		'0xD5' => '0xD7B1',		'0xD6' => '0xD7B2',
2720
				'0xE0' => '0xD790',		'0xEA' => '0xD79A',		'0xEC' => '0xD79C',
2721
				'0xED' => '0xD79D',		'0xEE' => '0xD79E',		'0xEF' => '0xD79F',
2722
				'0xF0' => '0xD7A0',		'0xF1' => '0xD7A1',		'0xF2' => '0xD7A2',
2723
				'0xF3' => '0xD7A3',		'0xF5' => '0xD7A5',		'0xF6' => '0xD7A6',
2724
				'0xF7' => '0xD7A7',		'0xF8' => '0xD7A8',		'0xF9' => '0xD7A9',
2725
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2726
				'0x86' => '0xE280A0',	'0x87' => '0xE280A1',	'0x89' => '0xE280B0',
2727
				'0x8B' => '0xE280B9',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2728
				'0x95' => '0xE280A2',	'0x97' => '0xE28094',	'0x99' => '0xE284A2',
2729
				'0xC0' => '0xD6B0',		'0xC1' => '0xD6B1',		'0xC3' => '0xD6B3',
2730
				'0xC4' => '0xD6B4',		'0xC5' => '0xD6B5',		'0xC6' => '0xD6B6',
2731
				'0xC7' => '0xD6B7',		'0xC8' => '0xD6B8',		'0xC9' => '0xD6B9',
2732
				'0xCB' => '0xD6BB',		'0xCC' => '0xD6BC',		'0xCD' => '0xD6BD',
2733
				'0xCE' => '0xD6BE',		'0xCF' => '0xD6BF',		'0xD0' => '0xD780',
2734
				'0xD2' => '0xD782',		'0xE3' => '0xD793',		'0xE4' => '0xD794',
2735
				'0xE5' => '0xD795',		'0xE7' => '0xD797',		'0xE9' => '0xD799',
2736
				'0xFD' => '0xE2808E',	'0xFE' => '0xE2808F',	'0x92' => '0xE28099',
2737
				'0x83' => '0xC692',		'0xD3' => '0xD783',		'0x88' => '0xCB86',
2738
				'0x98' => '0xCB9C',		'0x91' => '0xE28098',	'0x96' => '0xE28093',
2739
				'0xBA' => '0xC3B7',		'0x9B' => '0xE280BA',	'0xAA' => '0xC397',
2740
				'0xA4' => '0xE282AA',	'0xE1' => '0xD791',		'0xE6' => '0xD796',
2741
				'0xE8' => '0xD798',		'0xEB' => '0xD79B',		'0xF4' => '0xD7A4',
2742
				'0xFA' => '0xD7AA',
2743
			),
2744
			'windows-1253' => array(
2745
				'0x81' => '\'\'',			'0x88' => '\'\'',			'0x8A' => '\'\'',
2746
				'0x8C' => '\'\'',			'0x8D' => '\'\'',			'0x8E' => '\'\'',
2747
				'0x8F' => '\'\'',			'0x90' => '\'\'',			'0x98' => '\'\'',
2748
				'0x9A' => '\'\'',			'0x9C' => '\'\'',			'0x9D' => '\'\'',
2749
				'0x9E' => '\'\'',			'0x9F' => '\'\'',			'0xAA' => '\'\'',
2750
				'0xD2' => '0xE282AC',			'0xFF' => '0xCE92',			'0xCE' => '0xCE9E',
2751
				'0xB8' => '0xCE88',		'0xBA' => '0xCE8A',		'0xBC' => '0xCE8C',
2752
				'0xBE' => '0xCE8E',		'0xBF' => '0xCE8F',		'0xC0' => '0xCE90',
2753
				'0xC8' => '0xCE98',		'0xCA' => '0xCE9A',		'0xCC' => '0xCE9C',
2754
				'0xCD' => '0xCE9D',		'0xCF' => '0xCE9F',		'0xDA' => '0xCEAA',
2755
				'0xE8' => '0xCEB8',		'0xEA' => '0xCEBA',		'0xEC' => '0xCEBC',
2756
				'0xEE' => '0xCEBE',		'0xEF' => '0xCEBF',		'0xC2' => '0xFF',
2757
				'0xBD' => '0xC2BD',		'0xED' => '0xCEBD',		'0xB2' => '0xC2B2',
2758
				'0xA0' => '0xC2A0',		'0xA3' => '0xC2A3',		'0xA4' => '0xC2A4',
2759
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2760
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2761
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2762
				'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',		'0xB3' => '0xC2B3',
2763
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2764
				'0xBB' => '0xC2BB',		'0xE2' => '0xCEB2',		'0x80' => '0xD2',
2765
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2766
				'0x86' => '0xE280A0',	'0xA1' => '0xCE85',		'0xA2' => '0xCE86',
2767
				'0x87' => '0xE280A1',	'0x89' => '0xE280B0',	'0xB9' => '0xCE89',
2768
				'0x8B' => '0xE280B9',	'0x91' => '0xE28098',	'0x99' => '0xE284A2',
2769
				'0x92' => '0xE28099',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2770
				'0x95' => '0xE280A2',	'0x96' => '0xE28093',	'0x97' => '0xE28094',
2771
				'0x9B' => '0xE280BA',	'0xAF' => '0xE28095',	'0xB4' => '0xCE84',
2772
				'0xC1' => '0xCE91',		'0xC3' => '0xCE93',		'0xC4' => '0xCE94',
2773
				'0xC5' => '0xCE95',		'0xC6' => '0xCE96',		'0x83' => '0xC692',
2774
				'0xC7' => '0xCE97',		'0xC9' => '0xCE99',		'0xCB' => '0xCE9B',
2775
				'0xD0' => '0xCEA0',		'0xD1' => '0xCEA1',		'0xD3' => '0xCEA3',
2776
				'0xD4' => '0xCEA4',		'0xD5' => '0xCEA5',		'0xD6' => '0xCEA6',
2777
				'0xD7' => '0xCEA7',		'0xD8' => '0xCEA8',		'0xD9' => '0xCEA9',
2778
				'0xDB' => '0xCEAB',		'0xDC' => '0xCEAC',		'0xDD' => '0xCEAD',
2779
				'0xDE' => '0xCEAE',		'0xDF' => '0xCEAF',		'0xE0' => '0xCEB0',
2780
				'0xE1' => '0xCEB1',		'0xE3' => '0xCEB3',		'0xE4' => '0xCEB4',
2781
				'0xE5' => '0xCEB5',		'0xE6' => '0xCEB6',		'0xE7' => '0xCEB7',
2782
				'0xE9' => '0xCEB9',		'0xEB' => '0xCEBB',		'0xF0' => '0xCF80',
2783
				'0xF1' => '0xCF81',		'0xF2' => '0xCF82',		'0xF3' => '0xCF83',
2784
				'0xF4' => '0xCF84',		'0xF5' => '0xCF85',		'0xF6' => '0xCF86',
2785
				'0xF7' => '0xCF87',		'0xF8' => '0xCF88',		'0xF9' => '0xCF89',
2786
				'0xFA' => '0xCF8A',		'0xFB' => '0xCF8B',		'0xFC' => '0xCF8C',
2787
				'0xFD' => '0xCF8D',		'0xFE' => '0xCF8E',
2788
			),
2789
		);
2790
2791
		// Make some preparations.
2792
		if (isset($translation_tables[$upcontext['charset_detected']]))
2793
		{
2794
			$replace = '%field%';
2795
2796
			// Build a huge REPLACE statement...
2797
			foreach ($translation_tables[$upcontext['charset_detected']] as $from => $to)
2798
				$replace = 'REPLACE(' . $replace . ', ' . $from . ', ' . $to . ')';
2799
		}
2800
2801
		// Get a list of table names ahead of time... This makes it easier to set our substep and such
2802
		db_extend();
2803
		$queryTables = $smcFunc['db_list_tables'](false, $db_prefix . '%');
2804
2805
		$upcontext['table_count'] = count($queryTables);
2806
2807
		// What ones have we already done?
2808
		foreach ($queryTables as $id => $table)
2809
			if ($id < $_GET['substep'])
2810
				$upcontext['previous_tables'][] = $table;
2811
2812
		$upcontext['cur_table_num'] = $_GET['substep'];
2813
		$upcontext['cur_table_name'] = str_replace($db_prefix, '', $queryTables[$_GET['substep']]);
2814
		$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2815
2816
		// Make sure we're ready & have painted the template before proceeding
2817
		if ($support_js && !isset($_GET['xml'])) {
2818
			$_GET['substep'] = 0;
2819
			return false;
2820
		}
2821
2822
		// We want to start at the first table.
2823
		for ($substep = $_GET['substep'], $n = count($queryTables); $substep < $n; $substep++)
2824
		{
2825
			$table = $queryTables[$substep];
2826
2827
			$getTableStatus = $smcFunc['db_query']('', '
2828
				SHOW TABLE STATUS
2829
				LIKE {string:table_name}',
2830
				array(
2831
					'table_name' => str_replace('_', '\_', $table)
2832
				)
2833
			);
2834
2835
			// Only one row so we can just fetch_assoc and free the result...
2836
			$table_info = $smcFunc['db_fetch_assoc']($getTableStatus);
2837
			$smcFunc['db_free_result']($getTableStatus);
2838
2839
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($queryTables[$substep + 1]) ? $queryTables[$substep + 1] : $queryTables[$substep]));
2840
			$upcontext['cur_table_num'] = $substep + 1;
2841
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2842
2843
			// Do we need to pause?
2844
			nextSubstep($substep);
2845
2846
			// Just to make sure it doesn't time out.
2847
			if (function_exists('apache_reset_timeout'))
2848
				@apache_reset_timeout();
2849
2850
			$table_charsets = array();
2851
2852
			// Loop through each column.
2853
			$queryColumns = $smcFunc['db_query']('', '
2854
				SHOW FULL COLUMNS
2855
				FROM ' . $table_info['Name'],
2856
				array(
2857
				)
2858
			);
2859
			while ($column_info = $smcFunc['db_fetch_assoc']($queryColumns))
2860
			{
2861
				// Only text'ish columns have a character set and need converting.
2862
				if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false)
2863
				{
2864
					$collation = empty($column_info['Collation']) || $column_info['Collation'] === 'NULL' ? $table_info['Collation'] : $column_info['Collation'];
2865
					if (!empty($collation) && $collation !== 'NULL')
2866
					{
2867
						list($charset) = explode('_', $collation);
2868
2869
						// Build structure of columns to operate on organized by charset; only operate on columns not yet utf8
2870
						if ($charset != 'utf8') {
2871
							if (!isset($table_charsets[$charset]))
2872
								$table_charsets[$charset] = array();
2873
2874
							$table_charsets[$charset][] = $column_info;
2875
						}
2876
					}
2877
				}
2878
			}
2879
			$smcFunc['db_free_result']($queryColumns);
2880
2881
			// Only change the non-utf8 columns identified above
2882
			if (count($table_charsets) > 0)
2883
			{
2884
				$updates_blob = '';
2885
				$updates_text = '';
2886
				foreach ($table_charsets as $charset => $columns)
2887
				{
2888
					if ($charset !== $charsets[$upcontext['charset_detected']])
2889
					{
2890
						foreach ($columns as $column)
2891
						{
2892
							$updates_blob .= '
2893
								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'] . '\'') . ',';
2894
							$updates_text .= '
2895
								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'] . '\'') . ',';
2896
						}
2897
					}
2898
				}
2899
2900
				// Change the columns to binary form.
2901
				$smcFunc['db_query']('', '
2902
					ALTER TABLE {raw:table_name}{raw:updates_blob}',
2903
					array(
2904
						'table_name' => $table_info['Name'],
2905
						'updates_blob' => substr($updates_blob, 0, -1),
2906
					)
2907
				);
2908
2909
				// Convert the character set if MySQL has no native support for it.
2910
				if (isset($translation_tables[$upcontext['charset_detected']]))
2911
				{
2912
					$update = '';
2913
					foreach ($table_charsets as $charset => $columns)
2914
						foreach ($columns as $column)
2915
							$update .= '
2916
								' . $column['Field'] . ' = ' . strtr($replace, array('%field%' => $column['Field'])) . ',';
2917
2918
					$smcFunc['db_query']('', '
2919
						UPDATE {raw:table_name}
2920
						SET {raw:updates}',
2921
						array(
2922
							'table_name' => $table_info['Name'],
2923
							'updates' => substr($update, 0, -1),
2924
						)
2925
					);
2926
				}
2927
2928
				// Change the columns back, but with the proper character set.
2929
				$smcFunc['db_query']('', '
2930
					ALTER TABLE {raw:table_name}{raw:updates_text}',
2931
					array(
2932
						'table_name' => $table_info['Name'],
2933
						'updates_text' => substr($updates_text, 0, -1),
2934
					)
2935
				);
2936
			}
2937
2938
			// Now do the actual conversion (if still needed).
2939
			if ($charsets[$upcontext['charset_detected']] !== 'utf8')
2940
			{
2941
				if ($command_line)
2942
					echo 'Converting table ' . $table_info['Name'] . ' to UTF-8...';
2943
2944
				$smcFunc['db_query']('', '
2945
					ALTER TABLE {raw:table_name}
2946
					CONVERT TO CHARACTER SET utf8',
2947
					array(
2948
						'table_name' => $table_info['Name'],
2949
					)
2950
				);
2951
2952
				if ($command_line)
2953
					echo " done.\n";
2954
			}
2955
			// If this is XML to keep it nice for the user do one table at a time anyway!
2956
			if (isset($_GET['xml']) && $upcontext['cur_table_num'] < $upcontext['table_count'])
2957
				return upgradeExit();
2958
		}
2959
2960
		$prev_charset = empty($translation_tables[$upcontext['charset_detected']]) ? $charsets[$upcontext['charset_detected']] : $translation_tables[$upcontext['charset_detected']];
2961
2962
		$smcFunc['db_insert']('replace',
2963
			'{db_prefix}settings',
2964
			array('variable' => 'string', 'value' => 'string'),
2965
			array(array('global_character_set', 'UTF-8'), array('previousCharacterSet', $prev_charset)),
2966
			array('variable')
2967
		);
2968
2969
		// Store it in Settings.php too because it's needed before db connection.
2970
		// Hopefully this works...
2971
		require_once($sourcedir . '/Subs-Admin.php');
2972
		updateSettingsFile(array('db_character_set' => '\'utf8\''));
2973
2974
		// The conversion might have messed up some serialized strings. Fix them!
2975
		$request = $smcFunc['db_query']('', '
2976
			SELECT id_action, extra
2977
			FROM {db_prefix}log_actions
2978
			WHERE action IN ({string:remove}, {string:delete})',
2979
			array(
2980
				'remove' => 'remove',
2981
				'delete' => 'delete',
2982
			)
2983
		);
2984
		while ($row = $smcFunc['db_fetch_assoc']($request))
2985
		{
2986
			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)
2987
				$smcFunc['db_query']('', '
2988
					UPDATE {db_prefix}log_actions
2989
					SET extra = {string:extra}
2990
					WHERE id_action = {int:current_action}',
2991
					array(
2992
						'current_action' => $row['id_action'],
2993
						'extra' => $matches[1] . strlen($matches[3]) . ':"' . $matches[3] . '"' . $matches[4],
2994
					)
2995
				);
2996
		}
2997
		$smcFunc['db_free_result']($request);
2998
2999
		if ($upcontext['dropping_index'] && $command_line)
3000
		{
3001
			echo "\nYour fulltext search index was dropped to facilitate the conversion. You will need to recreate it.";
3002
			flush();
3003
		}
3004
	}
3005
	$_GET['substep'] = 0;
3006
	return false;
3007
}
3008
3009
function serialize_to_json()
3010
{
3011
	global $command_line, $smcFunc, $modSettings, $sourcedir, $upcontext, $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...
3012
3013
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'serialize_json_xml' : 'serialize_json';
3014
	// First thing's first - did we already do this?
3015
	if (!empty($modSettings['json_done']))
3016
	{
3017
		if ($command_line)
3018
			return DeleteUpgrade();
3019
		else
3020
			return true;
3021
	}
3022
3023
	// Done it already - js wise?
3024
	if (!empty($_POST['json_done']))
3025
		return true;
3026
3027
	// List of tables affected by this function
3028
	// 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...
3029
	// If 3rd item in array is true, it indicates that col1 could be empty...
3030
	$tables = array(
3031
		'background_tasks' => array('id_task', 'task_data'),
3032
		'log_actions' => array('id_action', 'extra'),
3033
		'log_online' => array('session', 'url'),
3034
		'log_packages' => array('id_install', 'db_changes', 'failed_steps', 'credits'),
3035
		'log_spider_hits' => array('id_hit', 'url'),
3036
		'log_subscribed' => array('id_sublog', 'pending_details'),
3037
		'pm_rules' => array('id_rule', 'criteria', 'actions'),
3038
		'qanda' => array('id_question', 'answers'),
3039
		'subscriptions' => array('id_subscribe', 'cost'),
3040
		'user_alerts' => array('id_alert', 'extra', true),
3041
		'user_drafts' => array('id_draft', 'to_list', true),
3042
		// These last two are a bit different - we'll handle those separately
3043
		'settings' => array(),
3044
		'themes' => array()
3045
	);
3046
3047
	// Set up some context stuff...
3048
	// Because we're not using numeric indices, we need this to figure out the current table name...
3049
	$keys = array_keys($tables);
3050
3051
	$upcontext['page_title'] = 'Converting to JSON';
3052
	$upcontext['table_count'] = count($keys);
3053
	$upcontext['cur_table_num'] = $_GET['substep'];
3054
	$upcontext['cur_table_name'] = isset($keys[$_GET['substep']]) ? $keys[$_GET['substep']] : $keys[0];
3055
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3056
3057
	foreach ($keys as $id => $table)
3058
		if ($id < $_GET['substep'])
3059
			$upcontext['previous_tables'][] = $table;
3060
3061
	if ($command_line)
3062
		echo 'Converting data from serialize() to json_encode().';
3063
3064
	if (!$support_js || isset($_GET['xml']))
3065
	{
3066
		// Fix the data in each table
3067
		for ($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
3068
		{
3069
			$upcontext['cur_table_name'] = isset($keys[$substep + 1]) ? $keys[$substep + 1] : $keys[$substep];
3070
			$upcontext['cur_table_num'] = $substep + 1;
3071
3072
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3073
3074
			// Do we need to pause?
3075
			nextSubstep($substep);
3076
3077
			// Initialize a few things...
3078
			$where = '';
3079
			$vars = array();
3080
			$table = $keys[$substep];
3081
			$info = $tables[$table];
3082
3083
			// Now the fun - build our queries and all that fun stuff
3084
			if ($table == 'settings')
3085
			{
3086
				// Now a few settings...
3087
				$serialized_settings = array(
3088
					'attachment_basedirectories',
3089
					'attachmentUploadDir',
3090
					'cal_today_birthday',
3091
					'cal_today_event',
3092
					'cal_today_holiday',
3093
					'displayFields',
3094
					'last_attachments_directory',
3095
					'memberlist_cache',
3096
					'search_custom_index_config',
3097
					'spider_name_cache'
3098
				);
3099
3100
				// Loop through and fix these...
3101
				$new_settings = array();
3102
				if ($command_line)
3103
					echo "\n" . 'Fixing some settings...';
3104
3105
				foreach ($serialized_settings as $var)
3106
				{
3107
					if (isset($modSettings[$var]))
3108
					{
3109
						// Attempt to unserialize the setting
3110
						$temp = @safe_unserialize($modSettings[$var]);
3111
						if (!$temp && $command_line)
3112
							echo "\n - Failed to unserialize the '" . $var . "' setting. Skipping.";
3113
						elseif ($temp !== false)
3114
							$new_settings[$var] = json_encode($temp);
3115
					}
3116
				}
3117
3118
				// Update everything at once
3119
				if (!function_exists('cache_put_data'))
3120
					require_once($sourcedir . '/Load.php');
3121
				updateSettings($new_settings, true);
3122
3123
				if ($command_line)
3124
					echo ' done.';
3125
			}
3126
			elseif ($table == 'themes')
3127
			{
3128
				// Finally, fix the admin prefs. Unfortunately this is stored per theme, but hopefully they only have one theme installed at this point...
3129
				$query = $smcFunc['db_query']('', '
3130
					SELECT id_member, id_theme, value FROM {db_prefix}themes
3131
					WHERE variable = {string:admin_prefs}',
3132
						array(
3133
							'admin_prefs' => 'admin_preferences'
3134
						)
3135
				);
3136
3137
				if ($smcFunc['db_num_rows']($query) != 0)
3138
				{
3139
					while ($row = $smcFunc['db_fetch_assoc']($query))
3140
					{
3141
						$temp = @safe_unserialize($row['value']);
3142
3143
						if ($command_line)
3144
						{
3145
							if ($temp === false)
3146
								echo "\n" . 'Unserialize of admin_preferences for user ' . $row['id_member'] . ' failed. Skipping.';
3147
							else
3148
								echo "\n" . 'Fixing admin preferences...';
3149
						}
3150
3151
						if ($temp !== false)
3152
						{
3153
							$row['value'] = json_encode($temp);
3154
3155
							// Even though we have all values from the table, UPDATE is still faster than REPLACE
3156
							$smcFunc['db_query']('', '
3157
								UPDATE {db_prefix}themes
3158
								SET value = {string:prefs}
3159
								WHERE id_theme = {int:theme}
3160
									AND id_member = {int:member}
3161
									AND variable = {string:admin_prefs}',
3162
								array(
3163
									'prefs' => $row['value'],
3164
									'theme' => $row['id_theme'],
3165
									'member' => $row['id_member'],
3166
									'admin_prefs' => 'admin_preferences'
3167
								)
3168
							);
3169
3170
							if ($command_line)
3171
								echo ' done.';
3172
						}
3173
					}
3174
3175
					$smcFunc['db_free_result']($query);
3176
				}
3177
			}
3178
			else
3179
			{
3180
				// First item is always the key...
3181
				$key = $info[0];
3182
				unset($info[0]);
3183
3184
				// Now we know what columns we have and such...
3185
				if (count($info) == 2 && $info[2] === true)
3186
				{
3187
					$col_select = $info[1];
3188
					$where = ' WHERE ' . $info[1] . ' != {empty}';
3189
				}
3190
				else
3191
				{
3192
					$col_select = implode(', ', $info);
3193
				}
3194
3195
				$query = $smcFunc['db_query']('', '
3196
					SELECT ' . $key . ', ' . $col_select . '
3197
					FROM {db_prefix}' . $table . $where,
3198
					array()
3199
				);
3200
3201
				if ($smcFunc['db_num_rows']($query) != 0)
3202
				{
3203
					if ($command_line)
3204
					{
3205
						echo "\n" . ' +++ Fixing the "' . $table . '" table...';
3206
						flush();
3207
					}
3208
3209
					while ($row = $smcFunc['db_fetch_assoc']($query))
3210
					{
3211
						$update = '';
3212
3213
						// We already know what our key is...
3214
						foreach ($info as $col)
3215
						{
3216
							if ($col !== true && $row[$col] != '')
3217
							{
3218
								$temp = @safe_unserialize($row[$col]);
3219
3220
								if ($temp === false && $command_line)
3221
								{
3222
									echo "\nFailed to unserialize " . $row[$col] . "... Skipping\n";
3223
								}
3224
								else
3225
								{
3226
									$row[$col] = json_encode($temp);
3227
3228
									// Build our SET string and variables array
3229
									$update .= (empty($update) ? '' : ', ') . $col . ' = {string:' . $col . '}';
3230
									$vars[$col] = $row[$col];
3231
								}
3232
							}
3233
						}
3234
3235
						$vars[$key] = $row[$key];
3236
3237
						// In a few cases, we might have empty data, so don't try to update in those situations...
3238
						if (!empty($update))
3239
						{
3240
							$smcFunc['db_query']('', '
3241
								UPDATE {db_prefix}' . $table . '
3242
								SET ' . $update . '
3243
								WHERE ' . $key . ' = {' . ($key == 'session' ? 'string' : 'int') . ':' . $key . '}',
3244
								$vars
3245
							);
3246
						}
3247
					}
3248
3249
					if ($command_line)
3250
						echo ' done.';
3251
3252
					// Free up some memory...
3253
					$smcFunc['db_free_result']($query);
3254
				}
3255
			}
3256
			// If this is XML to keep it nice for the user do one table at a time anyway!
3257
			if (isset($_GET['xml']))
3258
				return upgradeExit();
3259
		}
3260
3261
		if ($command_line)
3262
		{
3263
			echo "\n" . 'Successful.' . "\n";
3264
			flush();
3265
		}
3266
		$upcontext['step_progress'] = 100;
3267
3268
		// Last but not least, insert a dummy setting so we don't have to do this again in the future...
3269
		updateSettings(array('json_done' => true));
3270
3271
		$_GET['substep'] = 0;
3272
		// Make sure we move on!
3273
		if ($command_line)
3274
			return DeleteUpgrade();
3275
3276
		return true;
3277
	}
3278
3279
	// If this fails we just move on to deleting the upgrade anyway...
3280
	$_GET['substep'] = 0;
3281
	return false;
3282
}
3283
3284
/******************************************************************************
3285
******************* Templates are below this point ****************************
3286
******************************************************************************/
3287
3288
// This is what is displayed if there's any chmod to be done. If not it returns nothing...
3289
function template_chmod()
3290
{
3291
	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...
3292
3293
	// Don't call me twice!
3294
	if (!empty($upcontext['chmod_called']))
3295
		return;
3296
3297
	$upcontext['chmod_called'] = true;
3298
3299
	// Nothing?
3300
	if (empty($upcontext['chmod']['files']) && empty($upcontext['chmod']['ftp_error']))
3301
		return;
3302
3303
	// Was it a problem with Windows?
3304
	if (!empty($upcontext['chmod']['ftp_error']) && $upcontext['chmod']['ftp_error'] == 'total_mess')
3305
	{
3306
		echo '
3307
			<div class="error_message red">
3308
				The following files need to be writable to continue the upgrade. Please ensure the Windows permissions are correctly set to allow this:<br>
3309
				<ul style="margin: 2.5ex; font-family: monospace;">
3310
					<li>' . implode('</li>
3311
					<li>', $upcontext['chmod']['files']) . '</li>
3312
				</ul>
3313
			</div>';
3314
3315
		return false;
3316
	}
3317
3318
	echo '
3319
		<div class="panel">
3320
			<h2>', $txt['upgrade_ftp_login'], '</h2>
3321
			<h3>', $txt['upgrade_ftp_perms'], '</h3>
3322
			<script>
3323
				function warning_popup()
3324
				{
3325
					popup = window.open(\'\',\'popup\',\'height=150,width=400,scrollbars=yes\');
3326
					var content = popup.document;
3327
					content.write(\'<!DOCTYPE html>\n\');
3328
					content.write(\'<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>\n\t<head>\n\t\t<meta name="robots" content="noindex">\n\t\t\');
3329
					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\');
3330
					content.write(\'<div class="windowbg description">\n\t\t\t<h4>', $txt['upgrade_ftp_files'], '</h4>\n\t\t\t\');
3331
					content.write(\'<p>', implode('<br>\n\t\t\t', $upcontext['chmod']['files']), '</p>\n\t\t\t\');';
3332
3333
	if (isset($upcontext['systemos']) && $upcontext['systemos'] == 'linux')
3334
		echo '
3335
					content.write(\'<hr>\n\t\t\t\');
3336
					content.write(\'<p>', $txt['upgrade_ftp_shell'], '</p>\n\t\t\t\');
3337
					content.write(\'<tt># chmod a+w ', implode(' ', $upcontext['chmod']['files']), '</tt>\n\t\t\t\');';
3338
3339
	echo '
3340
					content.write(\'<a href="javascript:self.close();">close</a>\n\t\t</div>\n\t</body>\n</html>\');
3341
					content.close();
3342
				}
3343
			</script>';
3344
3345
	if (!empty($upcontext['chmod']['ftp_error']))
3346
		echo '
3347
			<div class="error_message red">
3348
				', $txt['upgrade_ftp_error'], '<br><br>
3349
				<code>', $upcontext['chmod']['ftp_error'], '</code>
3350
			</div>
3351
			<br>';
3352
3353
	if (empty($upcontext['chmod_in_form']))
3354
		echo '
3355
	<form action="', $upcontext['form_url'], '" method="post">';
3356
3357
	echo '
3358
		<table width="520" border="0" align="center" style="margin-bottom: 1ex;">
3359
			<tr>
3360
				<td width="26%" valign="top" class="textbox"><label for="ftp_server">', $txt['ftp_server'], ':</label></td>
3361
				<td>
3362
					<div style="float: right; margin-right: 1px;"><label for="ftp_port" class="textbox"><strong>', $txt['ftp_port'], ':&nbsp;</strong></label> <input type="text" size="3" name="ftp_port" id="ftp_port" value="', isset($upcontext['chmod']['port']) ? $upcontext['chmod']['port'] : '21', '"></div>
3363
					<input type="text" size="30" name="ftp_server" id="ftp_server" value="', isset($upcontext['chmod']['server']) ? $upcontext['chmod']['server'] : 'localhost', '" style="width: 70%;">
3364
					<div class="smalltext block">', $txt['ftp_server_info'], '</div>
3365
				</td>
3366
			</tr><tr>
3367
				<td width="26%" valign="top" class="textbox"><label for="ftp_username">', $txt['ftp_username'], ':</label></td>
3368
				<td>
3369
					<input type="text" size="50" name="ftp_username" id="ftp_username" value="', isset($upcontext['chmod']['username']) ? $upcontext['chmod']['username'] : '', '" style="width: 99%;">
3370
					<div class="smalltext block">', $txt['ftp_username_info'], '</div>
3371
				</td>
3372
			</tr><tr>
3373
				<td width="26%" valign="top" class="textbox"><label for="ftp_password">', $txt['ftp_password'], ':</label></td>
3374
				<td>
3375
					<input type="password" size="50" name="ftp_password" id="ftp_password" style="width: 99%;">
3376
					<div class="smalltext block">', $txt['ftp_password_info'], '</div>
3377
				</td>
3378
			</tr><tr>
3379
				<td width="26%" valign="top" class="textbox"><label for="ftp_path">', $txt['ftp_path'], ':</label></td>
3380
				<td style="padding-bottom: 1ex;">
3381
					<input type="text" size="50" name="ftp_path" id="ftp_path" value="', isset($upcontext['chmod']['path']) ? $upcontext['chmod']['path'] : '', '" style="width: 99%;">
3382
					<div class="smalltext block">', !empty($upcontext['chmod']['path']) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'], '</div>
3383
				</td>
3384
			</tr>
3385
		</table>
3386
3387
		<div class="righttext" style="margin: 1ex;"><input type="submit" value="', $txt['ftp_connect'], '" class="button"></div>
3388
	</div>';
3389
3390
	if (empty($upcontext['chmod_in_form']))
3391
		echo '
3392
	</form>';
3393
}
3394
3395
function template_upgrade_above()
3396
{
3397
	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...
3398
3399
	echo '<!DOCTYPE html>
3400
<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>
3401
	<head>
3402
		<meta charset="', isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8', '">
3403
		<meta name="robots" content="noindex">
3404
		<title>', $txt['upgrade_upgrade_utility'], '</title>
3405
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/index.css?alp21">
3406
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/install.css?alp21">
3407
		', $txt['lang_rtl'] == true ? '<link rel="stylesheet" href="' . $settings['default_theme_url'] . '/css/rtl.css?alp21">' : '', '
3408
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
3409
		<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
3410
		<script>
3411
			var smf_scripturl = \'', $upgradeurl, '\';
3412
			var smf_charset = \'', (empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'UTF-8' : $txt['lang_character_set']) : $modSettings['global_character_set']), '\';
3413
			var startPercent = ', $upcontext['overall_percent'], ';
3414
3415
			// This function dynamically updates the step progress bar - and overall one as required.
3416
			function updateStepProgress(current, max, overall_weight)
3417
			{
3418
				// What out the actual percent.
3419
				var width = parseInt((current / max) * 100);
3420
				if (document.getElementById(\'step_progress\'))
3421
				{
3422
					document.getElementById(\'step_progress\').style.width = width + "%";
3423
					setInnerHTML(document.getElementById(\'step_text\'), width + "%");
3424
				}
3425
				if (overall_weight && document.getElementById(\'overall_progress\'))
3426
				{
3427
					overall_width = parseInt(startPercent + width * (overall_weight / 100));
3428
					document.getElementById(\'overall_progress\').style.width = overall_width + "%";
3429
					setInnerHTML(document.getElementById(\'overall_text\'), overall_width + "%");
3430
				}
3431
			}
3432
		</script>
3433
	</head>
3434
	<body>
3435
	<div id="footerfix">
3436
		<div id="header">
3437
			<h1 class="forumtitle">', $txt['upgrade_upgrade_utility'], '</h1>
3438
			<img id="smflogo" src="', $settings['default_theme_url'], '/images/smflogo.svg" alt="Simple Machines Forum" title="Simple Machines Forum">
3439
		</div>
3440
	<div id="wrapper">
3441
		<div id="upper_section">
3442
			<div id="inner_section">
3443
				<div id="inner_wrap">
3444
				</div>
3445
			</div>
3446
		</div>
3447
		<div id="content_section">
3448
		<div id="main_content_section">
3449
			<div id="main_steps">
3450
				<h2>', $txt['upgrade_progress'], '</h2>
3451
				<ul>';
3452
3453
	foreach ($upcontext['steps'] as $num => $step)
3454
		echo '
3455
						<li class="', $num < $upcontext['current_step'] ? 'stepdone' : ($num == $upcontext['current_step'] ? 'stepcurrent' : 'stepwaiting'), '">', $txt['upgrade_step'], ' ', $step[0], ': ', $step[1], '</li>';
3456
3457
	echo '
3458
					</ul>
3459
			</div>
3460
3461
			<div id="progress_bar">
3462
				<div id="overall_text">', $upcontext['overall_percent'], '%</div>
3463
				<div id="overall_progress" style="width: ', $upcontext['overall_percent'], '%;">
3464
					<span>', $txt['upgrade_overall_progress'], '</span>
3465
				</div>
3466
			</div>';
3467
3468
	if (isset($upcontext['step_progress']))
3469
		echo '
3470
				<br>
3471
				<br>
3472
				<div id="progress_bar_step">
3473
					<div id="step_text">', $upcontext['step_progress'], '%</div>
3474
					<div id="step_progress" style="width: ', $upcontext['step_progress'], '%;background-color: #ffd000;">
3475
						<span>', $txt['upgrade_step_progress'], '</span>
3476
					</div>
3477
				</div>';
3478
3479
	echo '
3480
				<div id="substep_bar_div" class="smalltext" style="float: left;width: 50%;margin-top: 0.6em;display: ', isset($upcontext['substep_progress']) ? '' : 'none', ';">', isset($upcontext['substep_progress_name']) ? trim(strtr($upcontext['substep_progress_name'], array('.' => ''))) : '', ':</div>
3481
				<div id="substep_bar_div2" style="float: left;font-size: 8pt; height: 12pt; border: 1px solid black; background-color: white; width: 33%; margin: 0.6em auto 0 6em; display: ', isset($upcontext['substep_progress']) ? '' : 'none', ';">
3482
					<div id="substep_text" style="color: #000; position: absolute; margin-left: -5em;">', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : '', '%</div>
3483
					<div id="substep_progress" style="width: ', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%; height: 12pt; z-index: 1; background-color: #eebaf4;">&nbsp;</div>
3484
				</div>';
3485
3486
	// How long have we been running this?
3487
	$elapsed = time() - $upcontext['started'];
3488
	$mins = (int) ($elapsed / 60);
3489
	$seconds = $elapsed - $mins * 60;
3490
	echo '
3491
								<br> <br> <br> <br> <br>
3492
								<div class="smalltext" style="padding: 5px; text-align: center;"><br>', $txt['upgrade_time_elapsed'], ':
3493
									<span id="mins_elapsed">', $mins, '</span> ', $txt['upgrade_time_mins'], ', <span id="secs_elapsed">', $seconds, '</span> ', $txt['upgrade_time_secs'], '.
3494
								</div>';
3495
	echo '
3496
			</div>
3497
			</div>
3498
			<div id="main_screen" class="clear">
3499
				<h2>', $upcontext['page_title'], '</h2>
3500
				<div class="panel">
3501
					<div style="max-height: 360px; overflow: auto;">';
3502
}
3503
3504
function template_upgrade_below()
3505
{
3506
	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...
3507
3508
	if (!empty($upcontext['pause']))
3509
		echo '
3510
								<em>', $txt['upgrade_incomplete'], '.</em><br>
3511
3512
								<h2 style="margin-top: 2ex;">', $txt['upgrade_not_quite_done'], '</h2>
3513
								<h3>
3514
									', $txt['upgrade_paused_overload'], '
3515
								</h3>';
3516
3517
	if (!empty($upcontext['custom_warning']))
3518
		echo '
3519
								<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3520
									<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3521
									<strong style="text-decoration: underline;">', $txt['upgrade_note'], '</strong><br>
3522
									<div style="padding-left: 6ex;">', $upcontext['custom_warning'], '</div>
3523
								</div>';
3524
3525
	echo '
3526
								<div class="righttext" style="margin: 1ex;">';
3527
3528
	if (!empty($upcontext['continue']))
3529
		echo '
3530
									<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '"', $upcontext['continue'] == 2 ? ' disabled' : '', ' class="button">';
3531
	if (!empty($upcontext['skip']))
3532
		echo '
3533
									<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="dontSubmit = true; document.getElementById(\'contbutt\').disabled = \'disabled\'; return true;" class="button">';
3534
3535
	echo '
3536
								</div>
3537
							</form>
3538
						</div>
3539
				</div>
3540
			</div>
3541
			</div>
3542
		</div>
3543
		<div id="footer">
3544
			<ul>
3545
				<li class="copyright"><a href="https://www.simplemachines.org/" title="Simple Machines Forum" target="_blank" rel="noopener">SMF &copy; 2018, Simple Machines</a></li>
3546
			</ul>
3547
		</div>
3548
	</body>
3549
</html>';
3550
3551
	// Are we on a pause?
3552
	if (!empty($upcontext['pause']))
3553
	{
3554
		echo '
3555
		<script>
3556
			window.onload = doAutoSubmit;
3557
			var countdown = 3;
3558
			var dontSubmit = false;
3559
3560
			function doAutoSubmit()
3561
			{
3562
				if (countdown == 0 && !dontSubmit)
3563
					document.upform.submit();
3564
				else if (countdown == -1)
3565
					return;
3566
3567
				document.getElementById(\'contbutt\').value = "', $txt['upgrade_continue'], ' (" + countdown + ")";
3568
				countdown--;
3569
3570
				setTimeout("doAutoSubmit();", 1000);
3571
			}
3572
		</script>';
3573
	}
3574
}
3575
3576
function template_xml_above()
3577
{
3578
	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...
3579
3580
	echo '<', '?xml version="1.0" encoding="UTF-8"?', '>
3581
	<smf>';
3582
3583
	if (!empty($upcontext['get_data']))
3584
		foreach ($upcontext['get_data'] as $k => $v)
3585
			echo '
3586
		<get key="', $k, '">', $v, '</get>';
3587
}
3588
3589
function template_xml_below()
3590
{
3591
	echo '
3592
		</smf>';
3593
}
3594
3595
function template_error_message()
3596
{
3597
	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...
3598
3599
	echo '
3600
	<div class="error_message red">
3601
		', $upcontext['error_msg'], '
3602
		<br>
3603
		<a href="', $_SERVER['PHP_SELF'], '">Click here to try again.</a>
3604
	</div>';
3605
}
3606
3607
function template_welcome_message()
3608
{
3609
	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...
3610
3611
	echo '
3612
		<script src="https://www.simplemachines.org/smf/current-version.js?version=' . SMF_VERSION . '"></script>
3613
			<h3>', sprintf($txt['upgrade_ready_proceed'], SMF_VERSION), '</h3>
3614
	<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">
3615
		<input type="hidden" name="', $upcontext['login_token_var'], '" value="', $upcontext['login_token'], '">
3616
		<div id="version_warning" style="margin: 2ex; padding: 2ex; border: 2px dashed #a92174; color: black; background-color: #fbbbe2; display: none;">
3617
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3618
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3619
			<div style="padding-left: 6ex;">
3620
				', sprintf($txt['upgrade_warning_out_of_date'], SMF_VERSION), '
3621
			</div>
3622
		</div>';
3623
3624
	$upcontext['chmod_in_form'] = true;
3625
	template_chmod();
3626
3627
	// For large, pre 1.1 RC2 forums give them a warning about the possible impact of this upgrade!
3628
	if ($upcontext['is_large_forum'])
3629
		echo '
3630
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3631
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3632
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3633
			<div style="padding-left: 6ex;">
3634
				', $txt['upgrade_warning_lots_data'], '
3635
			</div>
3636
		</div>';
3637
3638
	// A warning message?
3639
	if (!empty($upcontext['warning']))
3640
		echo '
3641
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3642
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3643
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3644
			<div style="padding-left: 6ex;">
3645
				', $upcontext['warning'], '
3646
			</div>
3647
		</div>';
3648
3649
	// Paths are incorrect?
3650
	echo '
3651
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #804840; color: black; background-color: #fe5a44; ', (file_exists($settings['default_theme_dir'] . '/scripts/script.js') ? 'display: none;' : ''), '" id="js_script_missing_error">
3652
			<div style="float: left; width: 2ex; font-size: 2em; color: black;">!!</div>
3653
			<strong style="text-decoration: underline;">', $txt['upgrade_critical_error'], '</strong><br>
3654
			<div style="padding-left: 6ex;">
3655
				', $txt['upgrade_error_script_js'], '
3656
			</div>
3657
		</div>';
3658
3659
	// Is there someone already doing this?
3660
	if (!empty($upcontext['user']['id']) && (time() - $upcontext['started'] < 72600 || time() - $upcontext['updated'] < 3600))
3661
	{
3662
		$ago = time() - $upcontext['started'];
3663
		if ($ago < 60)
3664
			$ago = $ago . ' seconds';
3665
		elseif ($ago < 3600)
3666
			$ago = (int) ($ago / 60) . ' minutes';
3667
		else
3668
			$ago = (int) ($ago / 3600) . ' hours';
3669
3670
		$active = time() - $upcontext['updated'];
3671
		if ($active < 60)
3672
			$updated = $active . ' seconds';
3673
		elseif ($active < 3600)
3674
			$updated = (int) ($active / 60) . ' minutes';
3675
		else
3676
			$updated = (int) ($active / 3600) . ' hours';
3677
3678
		echo '
3679
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3680
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3681
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3682
			<div style="padding-left: 6ex;">
3683
				&quot;', $upcontext['user']['name'], '&quot; has been running the upgrade script for the last ', $ago, ' - and was last active ', $updated, ' ago.';
3684
3685
		if ($active < 600)
3686
			echo '
3687
				', $txt['upgrade_run_script'], ' ', $upcontext['user']['name'],' ', $txt['upgrade_run_script2'], '';
3688
3689
		if ($active > $upcontext['inactive_timeout'])
3690
			echo '
3691
				<br><br>',$txt['upgrade_run'], '';
3692
		else
3693
			echo '
3694
				<br><br>', $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!');
3695
3696
		echo '
3697
			</div>
3698
		</div>';
3699
	}
3700
3701
	echo '
3702
			<strong>', $txt['upgrade_admin_login'], ' ', $disable_security ? '(DISABLED)' : '', '</strong>
3703
			<h3>', $txt['upgrade_sec_login'], '</h3>
3704
			<table>
3705
				<tr valign="top">
3706
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>', $txt['upgrade_username'], '</strong></td>
3707
					<td>
3708
						<input type="text" name="user" value="', !empty($upcontext['username']) ? $upcontext['username'] : '', '"', $disable_security ? ' disabled' : '', '>';
3709
3710
	if (!empty($upcontext['username_incorrect']))
3711
		echo '
3712
						<div class="smalltext" style="color: red;">', $txt['upgrade_wrong_username'], '</div>';
3713
3714
	echo '
3715
					</td>
3716
				</tr>
3717
				<tr valign="top">
3718
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>', $txt['upgrade_password'], '</strong></td>
3719
					<td>
3720
						<input type="password" name="passwrd" value=""', $disable_security ? ' disabled' : '', '>
3721
						<input type="hidden" name="hash_passwrd" value="">';
3722
3723
	if (!empty($upcontext['password_failed']))
3724
		echo '
3725
						<div class="smalltext" style="color: red;">', $txt['upgrade_wrong_password'], '</div>';
3726
3727
	echo '
3728
					</td>
3729
				</tr>';
3730
3731
	// Can they continue?
3732
	if (!empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] >= $upcontext['inactive_timeout'] && $upcontext['user']['step'] > 1)
3733
	{
3734
		echo '
3735
				<tr>
3736
					<td colspan="2">
3737
						<label for="cont"><input type="checkbox" id="cont" name="cont" checked>', $txt['upgrade_continue_step'], '</label>
3738
					</td>
3739
				</tr>';
3740
	}
3741
3742
	echo '
3743
			</table><br>
3744
			<span class="smalltext">
3745
				', $txt['upgrade_bypass'], '
3746
			</span>
3747
			<input type="hidden" name="login_attempt" id="login_attempt" value="1">
3748
			<input type="hidden" name="js_works" id="js_works" value="0">';
3749
3750
	// Say we want the continue button!
3751
	$upcontext['continue'] = !empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] < $upcontext['inactive_timeout'] ? 2 : 1;
3752
3753
	// This defines whether javascript is going to work elsewhere :D
3754
	echo '
3755
		<script>
3756
			if (\'XMLHttpRequest\' in window && document.getElementById(\'js_works\'))
3757
				document.getElementById(\'js_works\').value = 1;
3758
3759
			// Latest version?
3760
			function smfCurrentVersion()
3761
			{
3762
				var smfVer, yourVer;
3763
3764
				if (!(\'smfVersion\' in window))
3765
					return;
3766
3767
				window.smfVersion = window.smfVersion.replace(/SMF\s?/g, \'\');
3768
3769
				smfVer = document.getElementById(\'smfVersion\');
3770
				yourVer = document.getElementById(\'yourVersion\');
3771
3772
				setInnerHTML(smfVer, window.smfVersion);
3773
3774
				var currentVersion = getInnerHTML(yourVer);
3775
				if (currentVersion < window.smfVersion)
3776
					document.getElementById(\'version_warning\').style.display = \'\';
3777
			}
3778
			addLoadEvent(smfCurrentVersion);
3779
3780
			// This checks that the script file even exists!
3781
			if (typeof(smfSelectText) == \'undefined\')
3782
				document.getElementById(\'js_script_missing_error\').style.display = \'\';
3783
3784
		</script>';
3785
}
3786
3787
function template_upgrade_options()
3788
{
3789
	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...
3790
3791
	echo '
3792
			<h3>', $txt['upgrade_areyouready'], '</h3>
3793
			<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">';
3794
3795
	// Warning message?
3796
	if (!empty($upcontext['upgrade_options_warning']))
3797
		echo '
3798
		<div style="margin: 1ex; padding: 1ex; border: 1px dashed #cc3344; color: black; background-color: #ffe4e9;">
3799
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3800
			<strong style="text-decoration: underline;">Warning!</strong><br>
3801
			<div style="padding-left: 4ex;">
3802
				', $upcontext['upgrade_options_warning'], '
3803
			</div>
3804
		</div>';
3805
3806
	echo '
3807
				<table>
3808
					<tr valign="top">
3809
						<td width="2%">
3810
							<input type="checkbox" name="backup" id="backup" value="1">
3811
						</td>
3812
						<td width="100%">
3813
							<label for="backup">', $txt['upgrade_backup_table'], ' &quot;backup_' . $db_prefix . '&quot;.</label> (', $txt['upgrade_recommended'], ')
3814
						</td>
3815
					</tr>
3816
					<tr valign="top">
3817
						<td width="2%">
3818
							<input type="checkbox" name="maint" id="maint" value="1" checked>
3819
						</td>
3820
						<td width="100%">
3821
							<label for="maint">', $txt['upgrade_maintenace'], '</label> <span class="smalltext">(<a href="#" onclick="document.getElementById(\'mainmess\').style.display = document.getElementById(\'mainmess\').style.display == \'\' ? \'none\' : \'\'">', $txt['upgrade_customize'], '</a>)</span>
3822
							<div id="mainmess" style="display: none;">
3823
								<strong class="smalltext">', $txt['upgrade_maintenance_title'], ' </strong><br>
3824
								<input type="text" name="maintitle" size="30" value="', htmlspecialchars($mtitle), '"><br>
3825
								<strong class="smalltext">', $txt['upgrade_maintenace_message'], ' </strong><br>
3826
								<textarea name="mainmessage" rows="3" cols="50">', htmlspecialchars($mmessage), '</textarea>
3827
							</div>
3828
						</td>
3829
					</tr>
3830
					<tr valign="top">
3831
						<td width="2%">
3832
							<input type="checkbox" name="debug" id="debug" value="1">
3833
						</td>
3834
						<td width="100%">
3835
							<label for="debug">'.$txt['upgrade_debug_info'], '</label>
3836
						</td>
3837
					</tr>
3838
					<tr valign="top">
3839
						<td width="2%">
3840
							<input type="checkbox" name="empty_error" id="empty_error" value="1">
3841
						</td>
3842
						<td width="100%">
3843
							<label for="empty_error">', $txt['upgrade_empty_errlog'], '</label>
3844
						</td>
3845
					</tr>';
3846
3847
	if (!empty($upcontext['karma_installed']['good']) || !empty($upcontext['karma_installed']['bad']))
3848
		echo '
3849
					<tr valign="top">
3850
						<td width="2%">
3851
							<input type="checkbox" name="delete_karma" id="delete_karma" value="1">
3852
						</td>
3853
						<td width="100%">
3854
							<label for="delete_karma">', $txt['upgrade_delete_karma'], '</label>
3855
						</td>
3856
					</tr>';
3857
3858
	echo '
3859
					<tr valign="top">
3860
						<td width="2%">
3861
							<input type="checkbox" name="stats" id="stats" value="1"', empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']) ? '' : ' checked="checked"', ' />
3862
						</td>
3863
						<td width="100%">
3864
							<label for="stat">
3865
								', $txt['upgrade_stats_collection'], '<br>
3866
								<span class="smalltext">', $txt['upgrade_stats_info'], '</a></span>
3867
							</label>
3868
						</td>
3869
					</tr>
3870
				</table>
3871
				<input type="hidden" name="upcont" value="1">';
3872
3873
	// We need a normal continue button here!
3874
	$upcontext['continue'] = 1;
3875
}
3876
3877
// Template for the database backup tool/
3878
function template_backup_database()
3879
{
3880
	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...
3881
3882
	echo '
3883
			<h3>', $txt['upgrade_wait'], '</h3>';
3884
3885
	echo '
3886
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
3887
			<input type="hidden" name="backup_done" id="backup_done" value="0">
3888
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
3889
			<div id="debug_section" style="height: ', ($is_debug ? '115' : '12') , 'px; overflow: auto;">
3890
			<span id="debuginfo"></span>
3891
			</div>';
3892
3893
	// Dont any tables so far?
3894
	if (!empty($upcontext['previous_tables']))
3895
		foreach ($upcontext['previous_tables'] as $table)
3896
			echo '
3897
			<br>', $txt['upgrade_completed_table'], ' &quot;', $table, '&quot;.';
3898
3899
	echo '
3900
			<h3 id="current_tab_div">', $txt['upgrade_current_table'], ' &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
3901
			<br><span id="commess" style="font-weight: bold; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">Backup Complete! Click Continue to Proceed.</span>';
3902
3903
	// Continue please!
3904
	$upcontext['continue'] = $support_js ? 2 : 1;
3905
3906
	// If javascript allows we want to do this using XML.
3907
	if ($support_js)
3908
	{
3909
		echo '
3910
		<script>
3911
			var lastTable = ', $upcontext['cur_table_num'], ';
3912
			function getNextTables()
3913
			{
3914
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
3915
			}
3916
3917
			// Got an update!
3918
			function onBackupUpdate(oXMLDoc)
3919
			{
3920
				var sCurrentTableName = "";
3921
				var iTableNum = 0;
3922
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
3923
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
3924
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
3925
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
3926
3927
				// Update the page.
3928
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
3929
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
3930
				lastTable = iTableNum;
3931
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
3932
3933
		// If debug flood the screen.
3934
		if ($is_debug)
3935
			echo '
3936
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
3937
3938
				if (document.getElementById(\'debug_section\').scrollHeight)
3939
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
3940
3941
		echo '
3942
				// Get the next update...
3943
				if (iTableNum == ', $upcontext['table_count'], ')
3944
				{
3945
					document.getElementById(\'commess\').style.display = "";
3946
					document.getElementById(\'current_tab_div\').style.display = "none";
3947
					document.getElementById(\'contbutt\').disabled = 0;
3948
					document.getElementById(\'backup_done\').value = 1;
3949
				}
3950
				else
3951
					getNextTables();
3952
			}
3953
			getNextTables();
3954
		//# sourceURL=dynamicScript-bkup.js
3955
		</script>';
3956
	}
3957
}
3958
3959
function template_backup_xml()
3960
{
3961
	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...
3962
3963
	echo '
3964
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
3965
}
3966
3967
// Here is the actual "make the changes" template!
3968
function template_database_changes()
3969
{
3970
	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...
3971
3972
	if (empty($is_debug) && !empty($upcontext['upgrade_status']['debug']))
3973
		$is_debug = true;
3974
3975
	echo '
3976
		<h3>', $txt['upgrade_db_changes'], '</h3>
3977
		<h4 style="font-style: italic;">', $txt['upgrade_db_patient'], '</h4>';
3978
3979
	echo '
3980
		<form action="', $upcontext['form_url'], '&amp;filecount=', $upcontext['file_count'], '" name="upform" id="upform" method="post">
3981
		<input type="hidden" name="database_done" id="database_done" value="0">';
3982
3983
	// No javascript looks rubbish!
3984
	if (!$support_js)
3985
	{
3986
		foreach ($upcontext['actioned_items'] as $num => $item)
3987
		{
3988
			if ($num != 0)
3989
				echo ' Successful!';
3990
			echo '<br>' . $item;
3991
		}
3992
		if (!empty($upcontext['changes_complete']))
3993
		{
3994
			if ($is_debug)
3995
			{
3996
				$active = time() - $upcontext['started'];
3997
				$hours = floor($active / 3600);
3998
				$minutes = intval(($active / 60) % 60);
3999
				$seconds = intval($active % 60);
4000
4001
				$totalTime = '';
4002
				if ($hours > 0)
4003
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
4004
				if ($minutes > 0)
4005
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
4006
				if ($seconds > 0)
4007
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
4008
			}
4009
4010
			if ($is_debug && !empty($totalTime))
4011
				echo ' Successful! Completed in ', $totalTime, '<br><br>';
4012
			else
4013
				echo ' Successful!<br><br>';
4014
4015
			echo '<span id="commess" style="font-weight: bold;">', $txt['upgrade_db_complete'], '</span><br>';
4016
		}
4017
	}
4018
	else
4019
	{
4020
		// Tell them how many files we have in total.
4021
		if ($upcontext['file_count'] > 1)
4022
			echo '
4023
		<strong id="info1">', $txt['upgrade_script'], ' <span id="file_done">', $upcontext['cur_file_num'], '</span> of ', $upcontext['file_count'], '.</strong>';
4024
4025
		echo '
4026
		<h3 id="info2"><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></h3>
4027
		<br><span id="commess" style="font-weight: bold; display: ', !empty($upcontext['changes_complete']) || $upcontext['current_debug_item_num'] == $upcontext['debug_items'] ? 'inline' : 'none', ';">', $txt['upgrade_db_complete2'], '</span>';
4028
4029
		if ($is_debug)
4030
		{
4031
			if ($upcontext['current_debug_item_num'] == $upcontext['debug_items'])
4032
			{
4033
				$active = time() - $upcontext['started'];
4034
				$hours = floor($active / 3600);
4035
				$minutes = intval(($active / 60) % 60);
4036
				$seconds = intval($active % 60);
4037
4038
				$totalTime = '';
4039
				if ($hours > 0)
4040
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
4041
				if ($minutes > 0)
4042
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
4043
				if ($seconds > 0)
4044
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
4045
			}
4046
4047
			echo '
4048
			<br><span id="upgradeCompleted">';
4049
4050
			if (!empty($totalTime))
4051
				echo 'Completed in ', $totalTime, '<br>';
4052
4053
			echo '</span>
4054
			<div id="debug_section" style="height: 59px; overflow: auto;">
4055
			<span id="debuginfo"></span>
4056
			</div>';
4057
		}
4058
	}
4059
4060
	// Place for the XML error message.
4061
	echo '
4062
		<div id="error_block" style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9; display: ', empty($upcontext['error_message']) ? 'none' : '', ';">
4063
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4064
			<strong style="text-decoration: underline;">', $txt['upgrade_error'], '</strong><br>
4065
			<div style="padding-left: 6ex;" id="error_message">', isset($upcontext['error_message']) ? $upcontext['error_message'] : $txt['upgrade_unknown_error'], '</div>
4066
		</div>';
4067
4068
	// We want to continue at some point!
4069
	$upcontext['continue'] = $support_js ? 2 : 1;
4070
4071
	// If javascript allows we want to do this using XML.
4072
	if ($support_js)
4073
	{
4074
		echo '
4075
		<script>
4076
			var lastItem = ', $upcontext['current_debug_item_num'], ';
4077
			var sLastString = "', strtr($upcontext['current_debug_item_name'], array('"' => '&quot;')), '";
4078
			var iLastSubStepProgress = -1;
4079
			var curFile = ', $upcontext['cur_file_num'], ';
4080
			var totalItems = 0;
4081
			var prevFile = 0;
4082
			var retryCount = 0;
4083
			var testvar = 0;
4084
			var timeOutID = 0;
4085
			var getData = "";
4086
			var debugItems = ', $upcontext['debug_items'], ';';
4087
4088
		if ($is_debug)
4089
			echo '
4090
			var upgradeStartTime = ' . $upcontext['started'] . ';';
4091
4092
		echo '
4093
			function getNextItem()
4094
			{
4095
				// We want to track this...
4096
				if (timeOutID)
4097
					clearTimeout(timeOutID);
4098
				timeOutID = window.setTimeout("retTimeout()", ', (10 * $timeLimitThreshold), '000);
4099
4100
				getXMLDocument(\'', $upcontext['form_url'], '&xml&filecount=', $upcontext['file_count'], '&substep=\' + lastItem + getData, onItemUpdate);
4101
			}
4102
4103
			// Got an update!
4104
			function onItemUpdate(oXMLDoc)
4105
			{
4106
				var sItemName = "";
4107
				var sDebugName = "";
4108
				var iItemNum = 0;
4109
				var iSubStepProgress = -1;
4110
				var iDebugNum = 0;
4111
				var bIsComplete = 0;
4112
				getData = "";
4113
4114
				// We\'ve got something - so reset the timeout!
4115
				if (timeOutID)
4116
					clearTimeout(timeOutID);
4117
4118
				// Assume no error at this time...
4119
				document.getElementById("error_block").style.display = "none";
4120
4121
				// Are we getting some duff info?
4122
				if (!oXMLDoc.getElementsByTagName("item")[0])
4123
				{
4124
					// Too many errors?
4125
					if (retryCount > 15)
4126
					{
4127
						document.getElementById("error_block").style.display = "";
4128
						setInnerHTML(document.getElementById("error_message"), "Error retrieving information on step: " + (sDebugName == "" ? sLastString : sDebugName));';
4129
4130
	if ($is_debug)
4131
		echo '
4132
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4133
4134
	echo '
4135
					}
4136
					else
4137
					{
4138
						retryCount++;
4139
						getNextItem();
4140
					}
4141
					return false;
4142
				}
4143
4144
				// Never allow loops.
4145
				if (curFile == prevFile)
4146
				{
4147
					retryCount++;
4148
					if (retryCount > 10)
4149
					{
4150
						document.getElementById("error_block").style.display = "";
4151
						setInnerHTML(document.getElementById("error_message"), "Upgrade script appears to be going into a loop - step: " + sDebugName);';
4152
4153
	if ($is_debug)
4154
		echo '
4155
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4156
4157
	echo '
4158
					}
4159
				}
4160
				retryCount = 0;
4161
4162
				for (var i = 0; i < oXMLDoc.getElementsByTagName("item")[0].childNodes.length; i++)
4163
					sItemName += oXMLDoc.getElementsByTagName("item")[0].childNodes[i].nodeValue;
4164
				for (var i = 0; i < oXMLDoc.getElementsByTagName("debug")[0].childNodes.length; i++)
4165
					sDebugName += oXMLDoc.getElementsByTagName("debug")[0].childNodes[i].nodeValue;
4166
				for (var i = 0; i < oXMLDoc.getElementsByTagName("get").length; i++)
4167
				{
4168
					getData += "&" + oXMLDoc.getElementsByTagName("get")[i].getAttribute("key") + "=";
4169
					for (var j = 0; j < oXMLDoc.getElementsByTagName("get")[i].childNodes.length; j++)
4170
					{
4171
						getData += oXMLDoc.getElementsByTagName("get")[i].childNodes[j].nodeValue;
4172
					}
4173
				}
4174
4175
				iItemNum = oXMLDoc.getElementsByTagName("item")[0].getAttribute("num");
4176
				iDebugNum = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("num"));
4177
				bIsComplete = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("complete"));
4178
				iSubStepProgress = parseFloat(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("percent"));
4179
				sLastString = sDebugName + " (Item: " + iDebugNum + ")";
4180
4181
				curFile = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("num"));
4182
				debugItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("debug_items"));
4183
				totalItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("items"));
4184
4185
				// If we have an error we haven\'t completed!
4186
				if (oXMLDoc.getElementsByTagName("error")[0] && bIsComplete)
4187
					iDebugNum = lastItem;
4188
4189
				// Do we have the additional progress bar?
4190
				if (iSubStepProgress != -1)
4191
				{
4192
					document.getElementById("substep_bar_div").style.display = "";
4193
					document.getElementById("substep_bar_div2").style.display = "";
4194
					document.getElementById("substep_progress").style.width = iSubStepProgress + "%";
4195
					setInnerHTML(document.getElementById("substep_text"), iSubStepProgress + "%");
4196
					setInnerHTML(document.getElementById("substep_bar_div"), sDebugName.replace(/\./g, "") + ":");
4197
				}
4198
				else
4199
				{
4200
					document.getElementById("substep_bar_div").style.display = "none";
4201
					document.getElementById("substep_bar_div2").style.display = "none";
4202
				}
4203
4204
				// Move onto the next item?
4205
				if (bIsComplete)
4206
					lastItem = iDebugNum;
4207
				else
4208
					lastItem = iDebugNum - 1;
4209
4210
				// Are we finished?
4211
				if (bIsComplete && iDebugNum == -1 && curFile >= ', $upcontext['file_count'], ')
4212
				{';
4213
4214
		if ($is_debug)
4215
			echo '
4216
					document.getElementById(\'debug_section\').style.display = "none";
4217
4218
					var upgradeFinishedTime = parseInt(oXMLDoc.getElementsByTagName("curtime")[0].childNodes[0].nodeValue);
4219
					var diffTime = upgradeFinishedTime - upgradeStartTime;
4220
					var diffHours = Math.floor(diffTime / 3600);
4221
					var diffMinutes = parseInt((diffTime / 60) % 60);
4222
					var diffSeconds = parseInt(diffTime % 60);
4223
4224
					var totalTime = "";
4225
					if (diffHours > 0)
4226
						totalTime = totalTime + diffHours + " hour" + (diffHours > 1 ? "s" : "") + " ";
4227
					if (diffMinutes > 0)
4228
						totalTime = totalTime + diffMinutes + " minute" + (diffMinutes > 1 ? "s" : "") + " ";
4229
					if (diffSeconds > 0)
4230
						totalTime = totalTime + diffSeconds + " second" + (diffSeconds > 1 ? "s" : "");
4231
4232
					setInnerHTML(document.getElementById("upgradeCompleted"), "Completed in " + totalTime);';
4233
4234
		echo '
4235
4236
					document.getElementById(\'commess\').style.display = "";
4237
					document.getElementById(\'contbutt\').disabled = 0;
4238
					document.getElementById(\'database_done\').value = 1;';
4239
4240
		if ($upcontext['file_count'] > 1)
4241
			echo '
4242
					document.getElementById(\'info1\').style.display = "none";';
4243
4244
		echo '
4245
					document.getElementById(\'info2\').style.display = "none";
4246
					updateStepProgress(100, 100, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4247
					return true;
4248
				}
4249
				// Was it the last step in the file?
4250
				else if (bIsComplete && iDebugNum == -1)
4251
				{
4252
					lastItem = 0;
4253
					prevFile = curFile;';
4254
4255
		if ($is_debug)
4256
			echo '
4257
					setOuterHTML(document.getElementById(\'debuginfo\'), \'Moving to next script file...done<br><span id="debuginfo"><\' + \'/span>\');';
4258
4259
		echo '
4260
					getNextItem();
4261
					return true;
4262
				}';
4263
4264
		// If debug scroll the screen.
4265
		if ($is_debug)
4266
			echo '
4267
				if (iLastSubStepProgress == -1)
4268
				{
4269
					// Give it consistent dots.
4270
					dots = sDebugName.match(/\./g);
4271
					numDots = dots ? dots.length : 0;
4272
					for (var i = numDots; i < 3; i++)
4273
						sDebugName += ".";
4274
					setOuterHTML(document.getElementById(\'debuginfo\'), sDebugName + \'<span id="debuginfo"><\' + \'/span>\');
4275
				}
4276
				iLastSubStepProgress = iSubStepProgress;
4277
4278
				if (bIsComplete)
4279
					setOuterHTML(document.getElementById(\'debuginfo\'), \'done<br><span id="debuginfo"><\' + \'/span>\');
4280
				else
4281
					setOuterHTML(document.getElementById(\'debuginfo\'), \'...<span id="debuginfo"><\' + \'/span>\');
4282
4283
				if (document.getElementById(\'debug_section\').scrollHeight)
4284
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4285
4286
		echo '
4287
				// Update the page.
4288
				setInnerHTML(document.getElementById(\'item_num\'), iItemNum);
4289
				setInnerHTML(document.getElementById(\'cur_item_name\'), sItemName);';
4290
4291
		if ($upcontext['file_count'] > 1)
4292
		{
4293
			echo '
4294
				setInnerHTML(document.getElementById(\'file_done\'), curFile);
4295
				setInnerHTML(document.getElementById(\'item_count\'), totalItems);';
4296
		}
4297
4298
		echo '
4299
				// Is there an error?
4300
				if (oXMLDoc.getElementsByTagName("error")[0])
4301
				{
4302
					var sErrorMsg = "";
4303
					for (var i = 0; i < oXMLDoc.getElementsByTagName("error")[0].childNodes.length; i++)
4304
						sErrorMsg += oXMLDoc.getElementsByTagName("error")[0].childNodes[i].nodeValue;
4305
					document.getElementById("error_block").style.display = "";
4306
					setInnerHTML(document.getElementById("error_message"), sErrorMsg);
4307
					return false;
4308
				}
4309
4310
				// Get the progress bar right.
4311
				barTotal = debugItems * ', $upcontext['file_count'], ';
4312
				barDone = (debugItems * (curFile - 1)) + lastItem;
4313
4314
				updateStepProgress(barDone, barTotal, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4315
4316
				// Finally - update the time here as it shows the server is responding!
4317
				curTime = new Date();
4318
				iElapsed = (curTime.getTime() / 1000 - ', $upcontext['started'], ');
4319
				mins = parseInt(iElapsed / 60);
4320
				secs = parseInt(iElapsed - mins * 60);
4321
				setInnerHTML(document.getElementById("mins_elapsed"), mins);
4322
				setInnerHTML(document.getElementById("secs_elapsed"), secs);
4323
4324
				getNextItem();
4325
				return true;
4326
			}
4327
4328
			// What if we timeout?!
4329
			function retTimeout(attemptAgain)
4330
			{
4331
				// Oh noes...
4332
				if (!attemptAgain)
4333
				{
4334
					document.getElementById("error_block").style.display = "";
4335
					setInnerHTML(document.getElementById("error_message"), "Server has not responded for ', ($timeLimitThreshold * 10), ' seconds. It may be worth waiting a little longer or otherwise please click <a href=\"#\" onclick=\"retTimeout(true); return false;\">here<" + "/a> to try this step again");
4336
				}
4337
				else
4338
				{
4339
					document.getElementById("error_block").style.display = "none";
4340
					getNextItem();
4341
				}
4342
			}';
4343
4344
		// Start things off assuming we've not errored.
4345
		if (empty($upcontext['error_message']))
4346
			echo '
4347
			getNextItem();';
4348
4349
		echo '
4350
		//# sourceURL=dynamicScript-dbch.js
4351
		</script>';
4352
	}
4353
	return;
4354
}
4355
4356
function template_database_xml()
4357
{
4358
	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...
4359
4360
	echo '
4361
	<file num="', $upcontext['cur_file_num'], '" items="', $upcontext['total_items'], '" debug_items="', $upcontext['debug_items'], '">', $upcontext['cur_file_name'], '</file>
4362
	<item num="', $upcontext['current_item_num'], '">', $upcontext['current_item_name'], '</item>
4363
	<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>';
4364
4365
	if (!empty($upcontext['error_message']))
4366
		echo '
4367
	<error>', $upcontext['error_message'], '</error>';
4368
4369
	if (!empty($upcontext['error_string']))
4370
		echo '
4371
	<sql>', $upcontext['error_string'], '</sql>';
4372
4373
	if ($is_debug)
4374
		echo '
4375
	<curtime>', time(), '</curtime>';
4376
}
4377
4378
// Template for the UTF-8 conversion step. Basically a copy of the backup stuff with slight modifications....
4379
function template_convert_utf8()
4380
{
4381
	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...
4382
4383
	echo '
4384
			<h3>', $txt['upgrade_wait2'], '</h3>';
4385
4386
	echo '
4387
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4388
			<input type="hidden" name="utf8_done" id="utf8_done" value="0">
4389
			<strong>', $txt['upgrade_completed'], ' <span id="tab_done">', $upcontext['cur_table_num'], '</span> ', $txt['upgrade_outof'], ' ', $upcontext['table_count'], ' ', $txt['upgrade_tables'], '</strong>
4390
			<div id="debug_section" style="height: ', ($is_debug ? '97' : '12') , 'px; overflow: auto;">
4391
			<span id="debuginfo"></span>
4392
			</div>';
4393
4394
	// Done any tables so far?
4395
	if (!empty($upcontext['previous_tables']))
4396
		foreach ($upcontext['previous_tables'] as $table)
4397
			echo '
4398
			<br>', $txt['upgrade_completed_table'], ' &quot;', $table, '&quot;.';
4399
4400
	echo '
4401
			<h3 id="current_tab_div">', $txt['upgrade_current_table'], ' &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>';
4402
4403
	// If we dropped their index, let's let them know
4404
	if ($upcontext['dropping_index'])
4405
		echo '
4406
				<br><span id="indexmsg" style="font-weight: bold; font-style: italic; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">', $txt['upgrade_fulltext'], '</span>';
4407
4408
	// Completion notification
4409
	echo '
4410
			<br><span id="commess" style="font-weight: bold; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">', $txt['upgrade_conversion_proceed'], '</span>';
4411
4412
	// Continue please!
4413
	$upcontext['continue'] = $support_js ? 2 : 1;
4414
4415
	// If javascript allows we want to do this using XML.
4416
	if ($support_js)
4417
	{
4418
		echo '
4419
		<script>
4420
			var lastTable = ', $upcontext['cur_table_num'], ';
4421
			function getNextTables()
4422
			{
4423
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onConversionUpdate);
4424
			}
4425
4426
			// Got an update!
4427
			function onConversionUpdate(oXMLDoc)
4428
			{
4429
				var sCurrentTableName = "";
4430
				var iTableNum = 0;
4431
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4432
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4433
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4434
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4435
4436
				// Update the page.
4437
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4438
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4439
				lastTable = iTableNum;
4440
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4441
4442
		// If debug flood the screen.
4443
		if ($is_debug)
4444
			echo '
4445
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4446
4447
				if (document.getElementById(\'debug_section\').scrollHeight)
4448
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4449
4450
		echo '
4451
				// Get the next update...
4452
				if (iTableNum == ', $upcontext['table_count'], ')
4453
				{
4454
					document.getElementById(\'commess\').style.display = "";
4455
					if (document.getElementById(\'indexmsg\') != null) {
4456
						document.getElementById(\'indexmsg\').style.display = "";
4457
					}
4458
					document.getElementById(\'current_tab_div\').style.display = "none";
4459
					document.getElementById(\'contbutt\').disabled = 0;
4460
					document.getElementById(\'utf8_done\').value = 1;
4461
				}
4462
				else
4463
					getNextTables();
4464
			}
4465
			getNextTables();
4466
		//# sourceURL=dynamicScript-conv.js
4467
		</script>';
4468
	}
4469
}
4470
4471
function template_convert_xml()
4472
{
4473
	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...
4474
4475
	echo '
4476
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4477
}
4478
4479
// Template for the database backup tool/
4480
function template_serialize_json()
4481
{
4482
	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...
4483
4484
	echo '
4485
			<h3>', $txt['upgrade_convert_datajson'], '</h3>';
4486
4487
	echo '
4488
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4489
			<input type="hidden" name="json_done" id="json_done" value="0">
4490
			<strong>', $txt['upgrade_completed'], ' <span id="tab_done">', $upcontext['cur_table_num'], '</span> ', $txt['upgrade_outof'], ' ', $upcontext['table_count'], ' ', $txt['upgrade_tables'], '</strong>
4491
			<div id="debug_section" style="height: ', ($is_debug ? '115' : '12') , 'px; overflow: auto;">
4492
			<span id="debuginfo"></span>
4493
			</div>';
4494
4495
	// Dont any tables so far?
4496
	if (!empty($upcontext['previous_tables']))
4497
		foreach ($upcontext['previous_tables'] as $table)
4498
			echo '
4499
			<br>', $txt['upgrade_completed_table'], ' &quot;', $table, '&quot;.';
4500
4501
	echo '
4502
			<h3 id="current_tab_div">', $txt['upgrade_current_table'], ' &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
4503
			<br><span id="commess" style="font-weight: bold; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">', $txt['upgrade_json_completed'], '</span>';
4504
4505
	// Try to make sure substep was reset.
4506
	if ($upcontext['cur_table_num'] == $upcontext['table_count'])
4507
		echo '
4508
			<input type="hidden" name="substep" id="substep" value="0">';
4509
4510
	// Continue please!
4511
	$upcontext['continue'] = $support_js ? 2 : 1;
4512
4513
	// If javascript allows we want to do this using XML.
4514
	if ($support_js)
4515
	{
4516
		echo '
4517
		<script>
4518
			var lastTable = ', $upcontext['cur_table_num'], ';
4519
			function getNextTables()
4520
			{
4521
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4522
			}
4523
4524
			// Got an update!
4525
			function onBackupUpdate(oXMLDoc)
4526
			{
4527
				var sCurrentTableName = "";
4528
				var iTableNum = 0;
4529
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4530
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4531
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4532
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4533
4534
				// Update the page.
4535
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4536
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4537
				lastTable = iTableNum;
4538
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4539
4540
		// If debug flood the screen.
4541
		if ($is_debug)
4542
			echo '
4543
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>', $txt['upgrade_completed_table'], ' &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4544
4545
				if (document.getElementById(\'debug_section\').scrollHeight)
4546
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4547
4548
		echo '
4549
				// Get the next update...
4550
				if (iTableNum == ', $upcontext['table_count'], ')
4551
				{
4552
					document.getElementById(\'commess\').style.display = "";
4553
					document.getElementById(\'current_tab_div\').style.display = "none";
4554
					document.getElementById(\'contbutt\').disabled = 0;
4555
					document.getElementById(\'json_done\').value = 1;
4556
				}
4557
				else
4558
					getNextTables();
4559
			}
4560
			getNextTables();
4561
		//# sourceURL=dynamicScript-json.js
4562
		</script>';
4563
	}
4564
}
4565
4566
function template_serialize_json_xml()
4567
{
4568
	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...
4569
4570
	echo '
4571
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4572
}
4573
4574
function template_upgrade_complete()
4575
{
4576
	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...
4577
4578
	echo '
4579
	<h3>', $txt['upgrade_done'], ' <a href="', $boardurl, '/index.php">', $txt['upgrade_done2'], '</a>.  ', $txt['upgrade_done3'], '</h3>
4580
	<form action="', $boardurl, '/index.php">';
4581
4582
	if (!empty($upcontext['can_delete_script']))
4583
		echo '
4584
			<label for="delete_self"><input type="checkbox" id="delete_self" onclick="doTheDelete(this);"> ', $txt['upgrade_delete_now'], '</label> ', $txt['upgrade_delete_server'], '
4585
			<script>
4586
				function doTheDelete(theCheck)
4587
				{
4588
					var theImage = document.getElementById ? document.getElementById("delete_upgrader") : document.all.delete_upgrader;
4589
4590
					theImage.src = "', $upgradeurl, '?delete=1&ts_" + (new Date().getTime());
4591
					theCheck.disabled = true;
4592
				}
4593
			</script>
4594
			<img src="', $settings['default_theme_url'], '/images/blank.png" alt="" id="delete_upgrader"><br>';
4595
4596
	$active = time() - $upcontext['started'];
4597
	$hours = floor($active / 3600);
4598
	$minutes = intval(($active / 60) % 60);
4599
	$seconds = intval($active % 60);
4600
4601
	if ($is_debug)
4602
	{
4603
		$totalTime = '';
4604
		if ($hours > 0)
4605
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
4606
		if ($minutes > 0)
4607
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
4608
		if ($seconds > 0)
4609
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
4610
	}
4611
4612
	if ($is_debug && !empty($totalTime))
4613
		echo '<br> ', $txt['upgrade_completed_time'], ' ', $totalTime, '<br><br>';
4614
4615
	echo '<br>
4616
			', $txt['upgrade_problems'], '<br>
4617
			<br>
4618
			', $txt['upgrade_luck'], '<br>
4619
			Simple Machines';
4620
}
4621
4622
/**
4623
 * Convert MySQL (var)char ip col to binary
4624
 *
4625
 * @param string $targetTable The table to perform the operation on
4626
 * @param string $oldCol The old column to gather data from
4627
 * @param string $newCol The new column to put data in
4628
 * @param int $limit The amount of entries to handle at once.
4629
 * @param int $setSize The amount of entries after which to update the database.
4630
 *
4631
 * newCol needs to be a varbinary(16) null able field
4632
 * @return bool
4633
 */
4634
function MySQLConvertOldIp($targetTable, $oldCol, $newCol, $limit = 50000, $setSize = 100)
4635
{
4636
	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...
4637
4638
	$current_substep = $_GET['substep'];
4639
4640
	if (empty($_GET['a']))
4641
		$_GET['a'] = 0;
4642
	$step_progress['name'] = 'Converting ips';
4643
	$step_progress['current'] = $_GET['a'];
4644
4645
	// Skip this if we don't have the column
4646
	$request = $smcFunc['db_query']('', '
4647
		SHOW FIELDS
4648
		FROM {db_prefix}{raw:table}
4649
		WHERE Field = {string:name}',
4650
		array(
4651
			'table' => $targetTable,
4652
			'name' => $oldCol,
4653
	));
4654
	if ($smcFunc['db_num_rows']($request) !== 1)
4655
	{
4656
		$smcFunc['db_free_result']($request);
4657
		return;
4658
	}
4659
	$smcFunc['db_free_result']($request);
4660
4661
	//mysql default max length is 1mb https://dev.mysql.com/doc/refman/5.1/en/packet-too-large.html
4662
	$arIp = array();
4663
4664
	$is_done = false;
4665
	while (!$is_done)
4666
	{
4667
		// Keep looping at the current step.
4668
		nextSubstep($current_substep);
4669
4670
		$arIp = array();
4671
4672
		$request = $smcFunc['db_query']('', '
4673
			SELECT DISTINCT {raw:old_col}
4674
			FROM {db_prefix}{raw:table_name}
4675
			WHERE {raw:new_col} IS NULL
4676
			LIMIT {int:limit}',
4677
			array(
4678
				'old_col' => $oldCol,
4679
				'new_col' => $newCol,
4680
				'table_name' => $targetTable,
4681
				'empty' => '',
4682
				'limit' => $limit,
4683
		));
4684
		while ($row = $smcFunc['db_fetch_assoc']($request))
4685
			$arIp[] = $row[$oldCol];
4686
		$smcFunc['db_free_result']($request);
4687
4688
		// Special case, null ip could keep us in a loop.
4689
		if (is_null($arIp[0]))
4690
			unset($arIp[0]);
4691
4692
		if (empty($arIp))
4693
			$is_done = true;
4694
4695
		$updates = array();
4696
		$cases = array();
4697
		$count = count($arIp);
4698
		for ($i = 0; $i < $count; $i++)
4699
		{
4700
			$arIp[$i] = trim($arIp[$i]);
4701
4702
			if (empty($arIp[$i]))
4703
				continue;
4704
4705
			$updates['ip' . $i] = $arIp[$i];
4706
			$cases[$arIp[$i]] = 'WHEN ' . $oldCol . ' = {string:ip' . $i . '} THEN {inet:ip' . $i . '}';
4707
4708
			if ($setSize > 0 && $i % $setSize === 0)
4709
			{
4710
				if (count($updates) == 1)
4711
					continue;
4712
4713
				$updates['whereSet'] = array_values($updates);
4714
				$smcFunc['db_query']('', '
4715
					UPDATE {db_prefix}' . $targetTable . '
4716
					SET ' . $newCol . ' = CASE ' .
4717
					implode('
4718
						', $cases) . '
4719
						ELSE NULL
4720
					END
4721
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4722
					$updates
4723
				);
4724
4725
				$updates = array();
4726
				$cases = array();
4727
			}
4728
		}
4729
4730
		// Incase some extras made it through.
4731
		if (!empty($updates))
4732
		{
4733
			if (count($updates) == 1)
4734
			{
4735
				foreach ($updates as $key => $ip)
4736
				{
4737
					$smcFunc['db_query']('', '
4738
						UPDATE {db_prefix}' . $targetTable . '
4739
						SET ' . $newCol . ' = {inet:ip}
4740
						WHERE ' . $oldCol . ' = {string:ip}',
4741
						array(
4742
							'ip' => $ip
4743
					));
4744
				}
4745
			}
4746
			else
4747
			{
4748
				$updates['whereSet'] = array_values($updates);
4749
				$smcFunc['db_query']('', '
4750
					UPDATE {db_prefix}' . $targetTable . '
4751
					SET ' . $newCol . ' = CASE ' .
4752
					implode('
4753
						', $cases) . '
4754
						ELSE NULL
4755
					END
4756
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4757
					$updates
4758
				);
4759
			}
4760
		}
4761
		else
4762
			$is_done = true;
4763
4764
		$_GET['a'] += $limit;
4765
		$step_progress['current'] = $_GET['a'];
4766
	}
4767
4768
	unset($_GET['a']);
4769
}
4770
4771
/**
4772
 * Get the column info. This is basically the same as smf_db_list_columns but we get 1 column, force detail and other checks.
4773
 *
4774
 * @param string $targetTable The table to perform the operation on
4775
 * @param string $column The column we are looking for.
4776
 *
4777
 * @return array Info on the table.
4778
 */
4779
function upgradeGetColumnInfo($targetTable, $column)
4780
{
4781
 	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...
4782
4783
 	// This should already be here, but be safe.
4784
 	db_extend('packages');
4785
4786
 	$columns = $smcFunc['db_list_columns']($targetTable, true);
4787
4788
	if (isset($columns[$column]))
4789
		return $columns[$column];
4790
	else
4791
		return null;
4792
}
4793
4794
?>
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected EOF
Loading history...