Completed
Pull Request — release-2.1 (#4470)
by Fran
12:04
created

upgrade.php ➔ serialize_to_json()   F

Complexity

Conditions 46
Paths 4926

Size

Total Lines 274
Code Lines 145

Duplication

Lines 3
Ratio 1.09 %

Importance

Changes 0
Metric Value
cc 46
eloc 145
nc 4926
nop 0
dl 3
loc 274
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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