Completed
Pull Request — release-2.1 (#4470)
by Fran
21:16
created

upgrade.php ➔ php_version_check()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines http://www.simplemachines.org
8
 * @copyright 2017 Simple Machines and individual contributors
9
 * @license http://www.simplemachines.org/about/smf/license.php BSD
10
 *
11
 * @version 2.1 Beta 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
85
// All the steps in detail.
86
// Number,Name,Function,Progress Weight.
87
$upcontext['steps'] = array(
88
	0 => array(1, $txt['upgrade_step_login'], 'WelcomeLogin', 2),
89
	1 => array(2, $txt['upgrade_step_options'], 'UpgradeOptions', 2),
90
	2 => array(3, $txt['upgrade_step_backup'], 'BackupDatabase', 10),
91
	3 => array(4, $txt['upgrade_step_database'], 'DatabaseChanges', 50),
92
	4 => array(5, $txt['upgrade_step_convertutf'], 'ConvertUtf8', 20),
93
	5 => array(6, $txt['upgrade_step_convertjson'], 'serialize_to_json', 10),
94
	6 => array(7, $txt['upgrade_step_delete'], 'DeleteUpgrade', 1),
95
);
96
// Just to remember which one has files in it.
97
$upcontext['database_step'] = 3;
98
@set_time_limit(600);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
99
if (!ini_get('safe_mode'))
100
{
101
	ini_set('mysql.connect_timeout', -1);
102
	ini_set('default_socket_timeout', 900);
103
}
104
// Clean the upgrade path if this is from the client.
105
if (!empty($_SERVER['argv']) && php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
106
	for ($i = 1; $i < $_SERVER['argc']; $i++)
107
	{
108
		if (preg_match('~^--path=(.+)$~', $_SERVER['argv'][$i], $match) != 0)
109
			$upgrade_path = substr($match[1], -1) == '/' ? substr($match[1], 0, -1) : $match[1];
110
	}
111
112
// Are we from the client?
113
if (php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
114
{
115
	$command_line = true;
116
	$disable_security = true;
117
}
118
else
119
	$command_line = false;
120
121
// Load this now just because we can.
122
require_once($upgrade_path . '/Settings.php');
123
124
// We don't use "-utf8" anymore...  Tweak the entry that may have been loaded by Settings.php
125
if (isset($language))
126
	$language = str_ireplace('-utf8', '', $language);
127
128
// Are we logged in?
129
if (isset($upgradeData))
130
{
131
	$upcontext['user'] = json_decode(base64_decode($upgradeData), true);
132
133
	// Check for sensible values.
134 View Code Duplication
	if (empty($upcontext['user']['started']) || $upcontext['user']['started'] < time() - 86400)
135
		$upcontext['user']['started'] = time();
136 View Code Duplication
	if (empty($upcontext['user']['updated']) || $upcontext['user']['updated'] < time() - 86400)
137
		$upcontext['user']['updated'] = 0;
138
139
	$upcontext['started'] = $upcontext['user']['started'];
140
	$upcontext['updated'] = $upcontext['user']['updated'];
141
142
	$is_debug = !empty($upcontext['user']['debug']) ? true : false;
143
}
144
145
// Nothing sensible?
146
if (empty($upcontext['updated']))
147
{
148
	$upcontext['started'] = time();
149
	$upcontext['updated'] = 0;
150
	$upcontext['user'] = array(
151
		'id' => 0,
152
		'name' => 'Guest',
153
		'pass' => 0,
154
		'started' => $upcontext['started'],
155
		'updated' => $upcontext['updated'],
156
	);
157
}
158
159
// Load up some essential data...
160
loadEssentialData();
161
162
// Are we going to be mimic'ing SSI at this point?
163
if (isset($_GET['ssi']))
164
{
165
	require_once($sourcedir . '/Errors.php');
166
	require_once($sourcedir . '/Logging.php');
167
	require_once($sourcedir . '/Load.php');
168
	require_once($sourcedir . '/Security.php');
169
	require_once($sourcedir . '/Subs-Package.php');
170
171
	// SMF isn't started up properly, but loadUserSettings calls our cookies.
172 View Code Duplication
	if (!isset($smcFunc['json_encode']))
173
	{
174
		$smcFunc['json_encode'] = 'json_encode';
175
		$smcFunc['json_decode'] = 'smf_json_decode';
176
	}
177
178
	loadUserSettings();
179
	loadPermissions();
180
}
181
182
// Include our helper functions.
183
require_once($sourcedir . '/Subs.php');
184
require_once($sourcedir . '/LogInOut.php');
185
186
// This only exists if we're on SMF ;)
187
if (isset($modSettings['smfVersion']))
188
{
189
	$request = $smcFunc['db_query']('', '
190
		SELECT variable, value
191
		FROM {db_prefix}themes
192
		WHERE id_theme = {int:id_theme}
193
			AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
194
		array(
195
			'id_theme' => 1,
196
			'theme_url' => 'theme_url',
197
			'theme_dir' => 'theme_dir',
198
			'images_url' => 'images_url',
199
			'db_error_skip' => true,
200
		)
201
	);
202 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
203
		$modSettings[$row['variable']] = $row['value'];
204
	$smcFunc['db_free_result']($request);
205
}
206
207
if (!isset($modSettings['theme_url']))
208
{
209
	$modSettings['theme_dir'] = $boarddir . '/Themes/default';
210
	$modSettings['theme_url'] = 'Themes/default';
211
	$modSettings['images_url'] = 'Themes/default/images';
212
}
213
if (!isset($settings['default_theme_url']))
214
	$settings['default_theme_url'] = $modSettings['theme_url'];
215
if (!isset($settings['default_theme_dir']))
216
	$settings['default_theme_dir'] = $modSettings['theme_dir'];
217
218
$upcontext['is_large_forum'] = (empty($modSettings['smfVersion']) || $modSettings['smfVersion'] <= '1.1 RC1') && !empty($modSettings['totalMessages']) && $modSettings['totalMessages'] > 75000;
219
// Default title...
220
$upcontext['page_title'] = 'Updating Your SMF Installation!';
221
222
// Have we got tracking data - if so use it (It will be clean!)
223
if (isset($_GET['data']))
224
{
225
	global $is_debug;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

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

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

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

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

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

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

Loading history...
319
	}
320
321
	// Handle the progress of the step, if any.
322
	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
323
	{
324
		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
325
		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
326
	}
327
	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
328
329
	// We usually dump our templates out.
330
	if (!$fallThrough)
331
	{
332
		// This should not happen my dear... HELP ME DEVELOPERS!!
333
		if (!empty($command_line))
334
		{
335
			if (function_exists('debug_print_backtrace'))
336
				debug_print_backtrace();
337
338
			echo "\n" . 'Error: Unexpected call to use the ' . (isset($upcontext['sub_template']) ? $upcontext['sub_template'] : '') . ' template. Please copy and paste all the text above and visit the SMF support forum to tell the Developers that they\'ve made a boo boo; they\'ll get you up and running again.';
0 ignored issues
show
Security Cross-Site Scripting introduced by
' ' . 'Error: Unexpected... up and running again.' can contain request data and is used in output context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 312
  2. $upcontext is assigned
    in other/upgrade.php on line 313
  3. $upcontext is assigned
    in other/upgrade.php on line 314
  4. $upcontext is assigned
    in other/upgrade.php on line 327

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
339
			flush();
340
			die();
341
		}
342
343
		if (!isset($_GET['xml']))
344
			template_upgrade_above();
345
		else
346
		{
347
			header('Content-Type: text/xml; charset=UTF-8');
348
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
349
			$upcontext['get_data'] = array();
350
			foreach ($_GET as $k => $v)
351
			{
352
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
353
				{
354
					$upcontext['get_data'][$k] = $v;
355
				}
356
			}
357
			template_xml_above();
358
		}
359
360
		// Call the template.
361
		if (isset($upcontext['sub_template']))
362
		{
363
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
364
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(json_encode($upcontext['upgrade_status']));
365
366
			// Custom stuff to pass back?
367
			if (!empty($upcontext['query_string']))
368
				$upcontext['form_url'] .= $upcontext['query_string'];
369
370
			// Call the appropriate subtemplate
371
			if (is_callable('template_' . $upcontext['sub_template']))
372
				call_user_func('template_' . $upcontext['sub_template']);
0 ignored issues
show
Security Code Execution introduced by
'template_' . $upcontext['sub_template'] can contain request data and is used in code execution context(s) leading to a potential security vulnerability.

3 paths for user data to reach this point

  1. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 312
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 312
  2. $upcontext is assigned
    in other/upgrade.php on line 313
  3. $upcontext is assigned
    in other/upgrade.php on line 314
  4. $upcontext is assigned
    in other/upgrade.php on line 327
  5. $upcontext is assigned
    in other/upgrade.php on line 363
  6. $upcontext is assigned
    in other/upgrade.php on line 364
  2. Path: Read from $_GET, and $v is assigned in other/upgrade.php on line 350
  1. Read from $_GET, and $v is assigned
    in other/upgrade.php on line 350
  2. $upcontext is assigned
    in other/upgrade.php on line 354
  3. $upcontext is assigned
    in other/upgrade.php on line 363
  4. $upcontext is assigned
    in other/upgrade.php on line 364
  3. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 364
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 364

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
373
			else
374
				die('Upgrade aborted!  Invalid template: template_' . $upcontext['sub_template']);
0 ignored issues
show
Security Cross-Site Scripting introduced by
'Upgrade aborted! Inval...context['sub_template'] can contain request data and is used in output context(s) leading to a potential security vulnerability.

3 paths for user data to reach this point

  1. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 312
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 312
  2. $upcontext is assigned
    in other/upgrade.php on line 313
  3. $upcontext is assigned
    in other/upgrade.php on line 314
  4. $upcontext is assigned
    in other/upgrade.php on line 327
  5. $upcontext is assigned
    in other/upgrade.php on line 363
  6. $upcontext is assigned
    in other/upgrade.php on line 364
  2. Path: Read from $_GET, and $v is assigned in other/upgrade.php on line 350
  1. Read from $_GET, and $v is assigned
    in other/upgrade.php on line 350
  2. $upcontext is assigned
    in other/upgrade.php on line 354
  3. $upcontext is assigned
    in other/upgrade.php on line 363
  4. $upcontext is assigned
    in other/upgrade.php on line 364
  3. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 364
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 364

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
375
		}
376
377
		// Was there an error?
378
		if (!empty($upcontext['forced_error_message']))
379
			echo $upcontext['forced_error_message'];
0 ignored issues
show
Security Cross-Site Scripting introduced by
$upcontext['forced_error_message'] can contain request data and is used in output context(s) leading to a potential security vulnerability.

3 paths for user data to reach this point

  1. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 312
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 312
  2. $upcontext is assigned
    in other/upgrade.php on line 313
  3. $upcontext is assigned
    in other/upgrade.php on line 314
  4. $upcontext is assigned
    in other/upgrade.php on line 327
  2. Path: Read from $_GET, and $v is assigned in other/upgrade.php on line 350
  1. Read from $_GET, and $v is assigned
    in other/upgrade.php on line 350
  2. $upcontext is assigned
    in other/upgrade.php on line 354
  3. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 364
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 364

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
380
381
		// Show the footer.
382
		if (!isset($_GET['xml']))
383
			template_upgrade_below();
384
		else
385
			template_xml_below();
386
	}
387
388
389
	if (!empty($command_line) && $is_debug)
390
	{
391
		$active = time() - $upcontext['started'];
392
		$hours = floor($active / 3600);
393
		$minutes = intval(($active / 60) % 60);
394
		$seconds = intval($active % 60);
395
396
		$totalTime = '';
397
		if ($hours > 0)
398
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
399
		if ($minutes > 0)
400
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
401
		if ($seconds > 0)
402
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
403
404
		if (!empty($totalTime))
405
			echo "\n" . '', $txt['upgrade_completed_time'], ' ' . $totalTime . "\n";
406
	}
407
408
	// Bang - gone!
409
	die();
410
}
411
412
// Used to direct the user to another location.
413
function redirectLocation($location, $addForm = true)
414
{
415
	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...
416
417
	// Command line users can't be redirected.
418
	if ($command_line)
419
		upgradeExit(true);
420
421
	// Are we providing the core info?
422
	if ($addForm)
423
	{
424
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
425
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
426
	}
427
428
	while (@ob_end_clean());
429
	header('Location: ' . strtr($location, array('&amp;' => '&')));
0 ignored issues
show
Security Response Splitting introduced by
'Location: ' . strtr($lo... array('&amp;' => '&')) can contain request data and is used in response header context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_GET, and $location is assigned
    in other/upgrade.php on line 425
  2. $location is passed through strtr()
    in other/upgrade.php on line 429

Response Splitting Attacks

Allowing an attacker to set a response header, opens your application to response splitting attacks; effectively allowing an attacker to send any response, he would like.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
430
431
	// Exit - saving status as we go.
432
	upgradeExit(true);
433
}
434
435
// Load all essential data and connect to the DB as this is pre SSI.php
436
function loadEssentialData()
437
{
438
	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...
439
	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...
440
441
	error_reporting(E_ALL);
442
	define('SMF', 1);
443
444
	// Start the session.
445
	if (@ini_get('session.save_handler') == 'user')
446
		@ini_set('session.save_handler', 'files');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

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

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
448
449
	if (empty($smcFunc))
450
		$smcFunc = array();
451
452
	// We need this for authentication and some upgrade code
453
	require_once($sourcedir . '/Subs-Auth.php');
454
	require_once($sourcedir . '/Class-Package.php');
455
456
	$smcFunc['strtolower'] = 'smf_strtolower';
457
458
	// Initialize everything...
459
	initialize_inputs();
460
461
	// Get the database going!
462
	if (empty($db_type) || $db_type == 'mysqli')
463
	{
464
		$db_type = 'mysql';
465
		// If overriding $db_type, need to set its settings.php entry too
466
		$changes = array();
467
		$changes['db_type'] = '\'mysql\'';
468
		require_once($sourcedir . '/Subs-Admin.php');
469
		updateSettingsFile($changes);
470
	}
471
472
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
473
	{
474
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
475
476
		// Make the connection...
477
		if (empty($db_connection))
478
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, array('non_fatal' => true));
479
		else
480
			// If we've returned here, ping/reconnect to be safe
481
			$smcFunc['db_ping']($db_connection);
482
483
		// Oh dear god!!
484
		if ($db_connection === null)
485
			die('Unable to connect to database - please check username and password are correct in Settings.php');
486
487
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
488
			$smcFunc['db_query']('', '
489
			SET NAMES {string:db_character_set}',
490
			array(
491
				'db_error_skip' => true,
492
				'db_character_set' => $db_character_set,
493
			)
494
		);
495
496
		// Load the modSettings data...
497
		$request = $smcFunc['db_query']('', '
498
			SELECT variable, value
499
			FROM {db_prefix}settings',
500
			array(
501
				'db_error_skip' => true,
502
			)
503
		);
504
		$modSettings = array();
505 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
506
			$modSettings[$row['variable']] = $row['value'];
507
		$smcFunc['db_free_result']($request);
508
	}
509
	else
510
	{
511
		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.');
512
	}
513
514
	require_once($sourcedir . '/Subs.php');
515
516
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
517
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
518
	{
519
		require_once($sourcedir . '/QueryString.php');
520
		cleanRequest();
521
	}
522
523
	if (!isset($_GET['substep']))
524
		$_GET['substep'] = 0;
525
}
526
527
function initialize_inputs()
528
{
529
	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...
530
531
	$start_time = time();
532
533
	umask(0);
534
535
	ob_start();
536
537
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
538
	ignore_user_abort(true);
539
540
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
541
	if (isset($_GET['delete']))
542
	{
543
		@unlink(__FILE__);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
551
552
		$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...
553
		while ($file = readdir($dh))
554
		{
555
			if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
556
				@unlink(dirname(__FILE__) . '/' . $file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
557
		}
558
		closedir($dh);
559
560
		// Legacy files while we're at it. NOTE: We only touch files we KNOW shouldn't be there.
561
		// 1.1 Sources files not in 2.0+
562
		@unlink(dirname(__FILE__) . '/Sources/ModSettings.php');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
563
		// 1.1 Templates that don't exist any more (e.g. renamed)
564
		@unlink(dirname(__FILE__) . '/Themes/default/Combat.template.php');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

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

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
566
		// 1.1 JS files were stored in the main theme folder, but in 2.0+ are in the scripts/ folder
567
		@unlink(dirname(__FILE__) . '/Themes/default/fader.js');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
576
577
		header('Location: http://' . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT']) . dirname($_SERVER['PHP_SELF']) . '/Themes/default/images/blank.png');
0 ignored issues
show
Security Response Splitting introduced by
'Location: http://' . (i...fault/images/blank.png' can contain request data and is used in response header context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Fetching key HTTP_HOST from $_SERVER
    in other/upgrade.php on line 577

Response Splitting Attacks

Allowing an attacker to set a response header, opens your application to response splitting attacks; effectively allowing an attacker to send any response, he would like.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
578
		exit;
579
	}
580
581
	// Something is causing this to happen, and it's annoying.  Stop it.
582
	$temp = 'upgrade_php?step';
583
	while (strlen($temp) > 4)
584
	{
585
		if (isset($_GET[$temp]))
586
			unset($_GET[$temp]);
587
		$temp = substr($temp, 1);
588
	}
589
590
	// Force a step, defaulting to 0.
591
	$_GET['step'] = (int) @$_GET['step'];
592
	$_GET['substep'] = (int) @$_GET['substep'];
593
}
594
595
// Step 0 - Let's welcome them in and ask them to login!
596
function WelcomeLogin()
597
{
598
	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...
599
	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...
600
601
	// We global $txt here so that the language files can add to them. This variable is NOT unused.
602
	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...
603
604
	$upcontext['sub_template'] = 'welcome_message';
605
606
	// Check for some key files - one template, one language, and a new and an old source file.
607
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
608
		&& @file_exists($sourcedir . '/QueryString.php')
609
		&& @file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')
610
		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
611
612
	// Need legacy scripts?
613 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.1)
614
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
615 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.0)
616
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-1.sql');
617 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 1.1)
618
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
619
620
	// We don't need "-utf8" files anymore...
621
	$upcontext['language'] = str_ireplace('-utf8', '', $upcontext['language']);
622
623
	// This needs to exist!
624
	if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
625
		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>]');
626
	else
627
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
628
629
	if (!$check)
630
		// 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.
631
		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.');
632
633
	// Do they meet the install requirements?
634
	if (!php_version_check())
635
		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.');
636
637
	if (!db_version_check())
638
		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.');
639
640
	// Do some checks to make sure they have proper privileges
641
	db_extend('packages');
642
643
	// CREATE
644
	$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');
645
646
	// ALTER
647
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
648
649
	// DROP
650
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
651
652
	// Sorry... we need CREATE, ALTER and DROP
653 View Code Duplication
	if (!$create || !$alter || !$drop)
654
		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.');
655
656
	// Do a quick version spot check.
657
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
658
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
659
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
660
		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.');
661
662
	// What absolutely needs to be writable?
663
	$writable_files = array(
664
		$boarddir . '/Settings.php',
665
		$boarddir . '/Settings_bak.php',
666
		$boarddir . '/db_last_error.php',
667
		$modSettings['theme_dir'] . '/css/minified.css',
668
		$modSettings['theme_dir'] . '/scripts/minified.js',
669
		$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
670
	);
671
672
	// Do we need to add this setting?
673
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
674
675
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] . '/custom_avatar';
676
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
677
678
	// This little fellow has to cooperate...
679
	quickFileWritable($custom_av_dir);
680
681
	// Are we good now?
682
	if (!is_writable($custom_av_dir))
683
		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));
684
	elseif ($need_settings_update)
685
	{
686
		if (!function_exists('cache_put_data'))
687
			require_once($sourcedir . '/Load.php');
688
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
689
		updateSettings(array('custom_avatar_url' => $custom_av_url));
690
	}
691
692
	require_once($sourcedir . '/Security.php');
693
694
	// Check the cache directory.
695
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
696
	if (!file_exists($cachedir_temp))
697
		@mkdir($cachedir_temp);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
698
	if (!file_exists($cachedir_temp))
699
		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.');
700
701
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
702
		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>.');
703
	elseif (!isset($_GET['skiplang']))
704
	{
705
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
706
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
707
708
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
709
			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>]');
710
	}
711
712
	if (!makeFilesWritable($writable_files))
713
		return false;
714
715
	// Check agreement.txt. (it may not exist, in which case $boarddir must be writable.)
716 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
717
		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.');
718
719
	// Upgrade the agreement.
720
	elseif (isset($modSettings['agreement']))
721
	{
722
		$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...
723
		fwrite($fp, $modSettings['agreement']);
724
		fclose($fp);
725
	}
726
727
	// We're going to check that their board dir setting is right in case they've been moving stuff around.
728
	if (strtr($boarddir, array('/' => '', '\\' => '')) != strtr(dirname(__FILE__), array('/' => '', '\\' => '')))
729
		$upcontext['warning'] = '
730
			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>
731
			<ul>
732
				<li>Board Directory: ' . $boarddir . '</li>
733
				<li>Source Directory: ' . $boarddir . '</li>
734
				<li>Cache Directory: ' . $cachedir_temp . '</li>
735
			</ul>
736
			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.';
737
738
	// Confirm mbstring is loaded...
739
	if (!extension_loaded('mbstring'))
740
		return throw_error($txt['install_no_mbstring']);
741
742
	// Check for https stream support.
743
	$supported_streams = stream_get_wrappers();
744
	if (!in_array('https', $supported_streams))
745
		$upcontext['custom_warning'] = $txt['install_no_https'];
746
747
	// Either we're logged in or we're going to present the login.
748
	if (checkLogin())
749
		return true;
750
751
	$upcontext += createToken('login');
752
753
	return false;
754
}
755
756
// Step 0.5: Does the login work?
757
function checkLogin()
758
{
759
	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...
760
	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...
761
762
	// Don't bother if the security is disabled.
763
	if ($disable_security)
764
		return true;
765
766
	// Are we trying to login?
767
	if (isset($_POST['contbutt']) && (!empty($_POST['user'])))
768
	{
769
		// If we've disabled security pick a suitable name!
770
		if (empty($_POST['user']))
771
			$_POST['user'] = 'Administrator';
772
773
		// Before 2.0 these column names were different!
774
		$oldDB = false;
775
		if (empty($db_type) || $db_type == 'mysql')
776
		{
777
			$request = $smcFunc['db_query']('', '
778
				SHOW COLUMNS
779
				FROM {db_prefix}members
780
				LIKE {string:member_name}',
781
				array(
782
					'member_name' => 'memberName',
783
					'db_error_skip' => true,
784
				)
785
			);
786
			if ($smcFunc['db_num_rows']($request) != 0)
787
				$oldDB = true;
788
			$smcFunc['db_free_result']($request);
789
		}
790
791
		// Get what we believe to be their details.
792
		if (!$disable_security)
793
		{
794
			if ($oldDB)
795
				$request = $smcFunc['db_query']('', '
796
					SELECT id_member, memberName AS member_name, passwd, id_group,
797
					additionalGroups AS additional_groups, lngfile
798
					FROM {db_prefix}members
799
					WHERE memberName = {string:member_name}',
800
					array(
801
						'member_name' => $_POST['user'],
802
						'db_error_skip' => true,
803
					)
804
				);
805
			else
806
				$request = $smcFunc['db_query']('', '
807
					SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile
808
					FROM {db_prefix}members
809
					WHERE member_name = {string:member_name}',
810
					array(
811
						'member_name' => $_POST['user'],
812
						'db_error_skip' => true,
813
					)
814
				);
815
			if ($smcFunc['db_num_rows']($request) != 0)
816
			{
817
				list ($id_member, $name, $password, $id_group, $addGroups, $user_language) = $smcFunc['db_fetch_row']($request);
818
819
				$groups = explode(',', $addGroups);
820
				$groups[] = $id_group;
821
822
				foreach ($groups as $k => $v)
823
					$groups[$k] = (int) $v;
824
825
				$sha_passwd = sha1(strtolower($name) . un_htmlspecialchars($_REQUEST['passwrd']));
826
827
				// We don't use "-utf8" anymore...
828
				$user_language = str_ireplace('-utf8', '', $user_language);
829
			}
830
			else
831
				$upcontext['username_incorrect'] = true;
832
			$smcFunc['db_free_result']($request);
833
		}
834
		$upcontext['username'] = $_POST['user'];
835
836
		// Track whether javascript works!
837
		if (!empty($_POST['js_works']))
838
		{
839
			$upcontext['upgrade_status']['js'] = 1;
840
			$support_js = 1;
841
		}
842
		else
843
			$support_js = 0;
844
845
		// Note down the version we are coming from.
846
		if (!empty($modSettings['smfVersion']) && empty($upcontext['user']['version']))
847
			$upcontext['user']['version'] = $modSettings['smfVersion'];
848
849
		// Didn't get anywhere?
850
		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']))
851
		{
852
			// MD5?
853
			$md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user']));
854
			if ($md5pass != $password)
855
			{
856
				$upcontext['password_failed'] = true;
857
				// Disable the hashing this time.
858
				$upcontext['disable_login_hashing'] = true;
859
			}
860
		}
861
862
		if ((empty($upcontext['password_failed']) && !empty($name)) || $disable_security)
863
		{
864
			// Set the password.
865
			if (!$disable_security)
866
			{
867
				// Do we actually have permission?
868
				if (!in_array(1, $groups))
869
				{
870
					$request = $smcFunc['db_query']('', '
871
						SELECT permission
872
						FROM {db_prefix}permissions
873
						WHERE id_group IN ({array_int:groups})
874
							AND permission = {string:admin_forum}',
875
						array(
876
							'groups' => $groups,
0 ignored issues
show
Bug introduced by
The variable $groups does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
877
							'admin_forum' => 'admin_forum',
878
							'db_error_skip' => true,
879
						)
880
					);
881
					if ($smcFunc['db_num_rows']($request) == 0)
882
						return throw_error('You need to be an admin to perform an upgrade!');
883
					$smcFunc['db_free_result']($request);
884
				}
885
886
				$upcontext['user']['id'] = $id_member;
887
				$upcontext['user']['name'] = $name;
888
			}
889
			else
890
			{
891
				$upcontext['user']['id'] = 1;
892
				$upcontext['user']['name'] = 'Administrator';
893
			}
894
			$upcontext['user']['pass'] = mt_rand(0, 60000);
895
			// This basically is used to match the GET variables to Settings.php.
896
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
897
898
			// Set the language to that of the user?
899
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
900
			{
901
				$user_language = basename($user_language, '.lng');
902
				$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
903
				preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
904
905
				if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
906
					$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'] . '.';
907
				elseif (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . basename($user_language, '.lng') . '.php'))
908
					$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'] . '.';
909
				else
910
				{
911
					// Set this as the new language.
912
					$upcontext['language'] = $user_language;
913
					$upcontext['upgrade_status']['lang'] = $upcontext['language'];
914
915
					// Include the file.
916
					require_once($modSettings['theme_dir'] . '/languages/Install.' . $user_language . '.php');
917
				}
918
			}
919
920
			// If we're resuming set the step and substep to be correct.
921
			if (isset($_POST['cont']))
922
			{
923
				$upcontext['current_step'] = $upcontext['user']['step'];
924
				$_GET['substep'] = $upcontext['user']['substep'];
925
			}
926
927
			return true;
928
		}
929
	}
930
931
	return false;
932
}
933
934
// Step 1: Do the maintenance and backup.
935
function UpgradeOptions()
936
{
937
	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...
938
	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...
939
940
	$upcontext['sub_template'] = 'upgrade_options';
941
	$upcontext['page_title'] = 'Upgrade Options';
942
943
	db_extend('packages');
944
	$upcontext['karma_installed'] = array('good' => false, 'bad' => false);
945
	$member_columns = $smcFunc['db_list_columns']('{db_prefix}members');
946
947
	$upcontext['karma_installed']['good'] = in_array('karma_good', $member_columns);
948
	$upcontext['karma_installed']['bad'] = in_array('karma_bad', $member_columns);
949
950
	unset($member_columns);
951
952
	// If we've not submitted then we're done.
953
	if (empty($_POST['upcont']))
954
		return false;
955
956
	// Firstly, if they're enabling SM stat collection just do it.
957
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']))
958
	{
959
		$upcontext['allow_sm_stats'] = true;
960
961
		// Don't register if we still have a key.
962
		if (empty($modSettings['sm_stats_key']))
963
		{
964
			// Attempt to register the site etc.
965
			$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...
966 View Code Duplication
			if ($fp)
967
			{
968
				$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
969
				$out .= 'Host: www.simplemachines.org' . "\r\n";
970
				$out .= 'Connection: Close' . "\r\n\r\n";
971
				fwrite($fp, $out);
972
973
				$return_data = '';
974
				while (!feof($fp))
975
					$return_data .= fgets($fp, 128);
976
977
				fclose($fp);
978
979
				// Get the unique site ID.
980
				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...
981
982
				if (!empty($ID[1]))
983
					$smcFunc['db_insert']('replace',
984
						$db_prefix . 'settings',
985
						array('variable' => 'string', 'value' => 'string'),
986
						array(
987
							array('sm_stats_key', $ID[1]),
988
							array('enable_sm_stats', 1),
989
						),
990
						array('variable')
991
					);
992
			}
993
		}
994
		else
995
		{
996
			$smcFunc['db_insert']('replace',
997
				$db_prefix . 'settings',
998
				array('variable' => 'string', 'value' => 'string'),
999
				array('enable_sm_stats', 1),
1000
				array('variable')
1001
			);
1002
		}
1003
	}
1004
	// Don't remove stat collection unless we unchecked the box for real, not from the loop.
1005 View Code Duplication
	elseif (empty($_POST['stats']) && empty($upcontext['allow_sm_stats']))
1006
		$smcFunc['db_query']('', '
1007
			DELETE FROM {db_prefix}settings
1008
			WHERE variable = {string:enable_sm_stats}',
1009
			array(
1010
				'enable_sm_stats' => 'enable_sm_stats',
1011
				'db_error_skip' => true,
1012
			)
1013
		);
1014
1015
	// Deleting old karma stuff?
1016
	if (!empty($_POST['delete_karma']))
1017
	{
1018
		// Delete old settings vars.
1019
		$smcFunc['db_query']('', '
1020
			DELETE FROM {db_prefix}settings
1021
			WHERE variable IN ({array_string:karma_vars})',
1022
			array(
1023
				'karma_vars' => array('karmaMode', 'karmaTimeRestrictAdmins', 'karmaWaitTime', 'karmaMinPosts', 'karmaLabel', 'karmaSmiteLabel', 'karmaApplaudLabel'),
1024
			)
1025
		);
1026
1027
		// Cleaning up old karma member settings.
1028
		if ($upcontext['karma_installed']['good'])
1029
			$smcFunc['db_query']('', '
1030
				ALTER TABLE {db_prefix}members
1031
				DROP karma_good',
1032
				array()
1033
			);
1034
1035
		// Does karma bad was enable?
1036
		if ($upcontext['karma_installed']['bad'])
1037
			$smcFunc['db_query']('', '
1038
				ALTER TABLE {db_prefix}members
1039
				DROP karma_bad',
1040
				array()
1041
			);
1042
1043
		// Cleaning up old karma permissions.
1044
		$smcFunc['db_query']('', '
1045
			DELETE FROM {db_prefix}permissions
1046
			WHERE permission = {string:karma_vars}',
1047
			array(
1048
				'karma_vars' => 'karma_edit',
1049
			)
1050
		);
1051
		// Cleaning up old log_karma table
1052
		$smcFunc['db_query']('', '
1053
			DROP TABLE IF EXISTS {db_prefix}log_karma',
1054
			array()
1055
		);
1056
	}
1057
1058
	// Emptying the error log?
1059
	if (!empty($_POST['empty_error']))
1060
		$smcFunc['db_query']('truncate_table', '
1061
			TRUNCATE {db_prefix}log_errors',
1062
			array(
1063
			)
1064
		);
1065
1066
	$changes = array();
1067
1068
	// Add proxy settings.
1069
	if (!isset($GLOBALS['image_proxy_maxsize']))
1070
		$changes += array(
1071
			'image_proxy_secret' => '\'' . substr(sha1(mt_rand()), 0, 20) . '\'',
1072
			'image_proxy_maxsize' => 5190,
1073
			'image_proxy_enabled' => 0,
1074
		);
1075
1076
	// If $boardurl reflects https, set force_ssl
1077
	if (!function_exists('cache_put_data'))
1078
		require_once($sourcedir . '/Load.php');
1079
	if (stripos($boardurl, 'https://') !== false)
1080
		updateSettings(array('force_ssl' => '2'));
1081
1082
	// If we're overriding the language follow it through.
1083
	if (isset($_GET['lang']) && file_exists($modSettings['theme_dir'] . '/languages/index.' . $_GET['lang'] . '.php'))
1084
		$changes['language'] = '\'' . $_GET['lang'] . '\'';
1085
1086
	if (!empty($_POST['maint']))
1087
	{
1088
		$changes['maintenance'] = '2';
1089
		// Remember what it was...
1090
		$upcontext['user']['main'] = $maintenance;
1091
1092
		if (!empty($_POST['maintitle']))
1093
		{
1094
			$changes['mtitle'] = '\'' . addslashes($_POST['maintitle']) . '\'';
1095
			$changes['mmessage'] = '\'' . addslashes($_POST['mainmessage']) . '\'';
1096
		}
1097
		else
1098
		{
1099
			$changes['mtitle'] = '\'Upgrading the forum...\'';
1100
			$changes['mmessage'] = '\'Don\\\'t worry, we will be back shortly with an updated forum.  It will only be a minute ;).\'';
1101
		}
1102
	}
1103
1104
	if ($command_line)
1105
		echo ' * Updating Settings.php...';
1106
1107
	// Fix some old paths.
1108
	if (substr($boarddir, 0, 1) == '.')
1109
		$changes['boarddir'] = '\'' . fixRelativePath($boarddir) . '\'';
1110
1111
	if (substr($sourcedir, 0, 1) == '.')
1112
		$changes['sourcedir'] = '\'' . fixRelativePath($sourcedir) . '\'';
1113
1114
	if (empty($cachedir) || substr($cachedir, 0, 1) == '.')
1115
		$changes['cachedir'] = '\'' . fixRelativePath($boarddir) . '/cache\'';
1116
1117
	// If they have a "host:port" setup for the host, split that into separate values
1118
	// You should never have a : in the hostname if you're not on MySQL, but better safe than sorry
1119
	if (strpos($db_server, ':') !== false && $db_type == 'mysql')
1120
	{
1121
		list ($db_server, $db_port) = explode(':', $db_server);
1122
1123
		$changes['db_server'] = '\'' . $db_server . '\'';
1124
1125
		// Only set this if we're not using the default port
1126
		if ($db_port != ini_get('mysqli.default_port'))
1127
			$changes['db_port'] = (int) $db_port;
1128
	}
1129
	elseif (!empty($db_port))
0 ignored issues
show
Bug introduced by
The variable $db_port seems only to be defined at a later point. As such the call to empty() seems to always evaluate to true.

This check marks calls to isset(...) or empty(...) that are found before the variable itself is defined. These will always have the same result.

This is likely the result of code being shifted around. Consider removing these calls.

Loading history...
1130
	{
1131
		// If db_port is set and is the same as the default, set it to ''
1132
		if ($db_type == 'mysql')
1133
		{
1134
			if ($db_port == ini_get('mysqli.default_port'))
1135
				$changes['db_port'] = '\'\'';
1136
			elseif ($db_type == 'postgresql' && $db_port == 5432)
1137
				$changes['db_port'] = '\'\'';
1138
		}
1139
	}
1140
1141
	// Maybe we haven't had this option yet?
1142
	if (empty($packagesdir))
1143
		$changes['packagesdir'] = '\'' . fixRelativePath($boarddir) . '/Packages\'';
1144
1145
	// Add support for $tasksdir var.
1146
	if (empty($tasksdir))
1147
		$changes['tasksdir'] = '\'' . fixRelativePath($sourcedir) . '/tasks\'';
1148
1149
	// Make sure we fix the language as well.
1150
	if (stristr($language, '-utf8'))
1151
		$changes['language'] = '\'' . str_ireplace('-utf8', '', $language) . '\'';
1152
1153
	// @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...
1154
1155
	// Update Settings.php with the new settings.
1156
	require_once($sourcedir . '/Subs-Admin.php');
1157
	updateSettingsFile($changes);
1158
1159
	if ($command_line)
1160
		echo ' Successful.' . "\n";
1161
1162
	// Are we doing debug?
1163
	if (isset($_POST['debug']))
1164
	{
1165
		$upcontext['upgrade_status']['debug'] = true;
1166
		$is_debug = true;
1167
	}
1168
1169
	// If we're not backing up then jump one.
1170
	if (empty($_POST['backup']))
1171
		$upcontext['current_step']++;
1172
1173
	// If we've got here then let's proceed to the next step!
1174
	return true;
1175
}
1176
1177
// Backup the database - why not...
1178
function BackupDatabase()
1179
{
1180
	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...
1181
1182
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1183
	$upcontext['page_title'] = 'Backup Database';
1184
1185
	// Done it already - js wise?
1186
	if (!empty($_POST['backup_done']))
1187
		return true;
1188
1189
	// Some useful stuff here.
1190
	db_extend();
1191
1192
	// Might need this as well
1193
	db_extend('packages');
1194
1195
	// Get all the table names.
1196
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1197
	$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...
1198
	$tables = $smcFunc['db_list_tables']($db, $filter);
1199
1200
	$table_names = array();
1201
	foreach ($tables as $table)
1202
		if (substr($table, 0, 7) !== 'backup_')
1203
			$table_names[] = $table;
1204
1205
	$upcontext['table_count'] = count($table_names);
1206
	$upcontext['cur_table_num'] = $_GET['substep'];
1207
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1208
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1209
	// For non-java auto submit...
1210
	$file_steps = $upcontext['table_count'];
1211
1212
	// What ones have we already done?
1213 View Code Duplication
	foreach ($table_names as $id => $table)
1214
		if ($id < $_GET['substep'])
1215
			$upcontext['previous_tables'][] = $table;
1216
1217
	if ($command_line)
1218
		echo 'Backing Up Tables.';
1219
1220
	// If we don't support javascript we backup here.
1221
	if (!$support_js || isset($_GET['xml']))
1222
	{
1223
		// Backup each table!
1224
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1225
		{
1226
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1227
			$upcontext['cur_table_num'] = $substep + 1;
1228
1229
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1230
1231
			// Do we need to pause?
1232
			nextSubstep($substep);
1233
1234
			backupTable($table_names[$substep]);
1235
1236
			// If this is XML to keep it nice for the user do one table at a time anyway!
1237
			if (isset($_GET['xml']))
1238
				return upgradeExit();
1239
		}
1240
1241
		if ($command_line)
1242
		{
1243
			echo "\n" . ' Successful.\'' . "\n";
1244
			flush();
1245
		}
1246
		$upcontext['step_progress'] = 100;
1247
1248
		$_GET['substep'] = 0;
1249
		// Make sure we move on!
1250
		return true;
1251
	}
1252
1253
	// Either way next place to post will be database changes!
1254
	$_GET['substep'] = 0;
1255
	return false;
1256
}
1257
1258
// Backup one table...
1259
function backupTable($table)
1260
{
1261
	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...
1262
1263
	if ($command_line)
1264
	{
1265
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1266
		flush();
1267
	}
1268
1269
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1270
1271
	if ($command_line)
1272
		echo ' done.';
1273
}
1274
1275
// Step 2: Everything.
1276
function DatabaseChanges()
1277
{
1278
	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...
1279
	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...
1280
1281
	// Have we just completed this?
1282
	if (!empty($_POST['database_done']))
1283
		return true;
1284
1285
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1286
	$upcontext['page_title'] = 'Database Changes';
1287
1288
	// All possible files.
1289
	// Name, < version, insert_on_complete
1290
	$files = array(
1291
		array('upgrade_1-0.sql', '1.1', '1.1 RC0'),
1292
		array('upgrade_1-1.sql', '2.0', '2.0 a'),
1293
		array('upgrade_2-0_' . $db_type . '.sql', '2.1', '2.1 dev0'),
1294
		array('upgrade_2-1_' . $db_type . '.sql', '3.0', SMF_VERSION),
1295
	);
1296
1297
	// How many files are there in total?
1298
	if (isset($_GET['filecount']))
1299
		$upcontext['file_count'] = (int) $_GET['filecount'];
1300
	else
1301
	{
1302
		$upcontext['file_count'] = 0;
1303
		foreach ($files as $file)
1304
		{
1305
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1306
				$upcontext['file_count']++;
1307
		}
1308
	}
1309
1310
	// Do each file!
1311
	$did_not_do = count($files) - $upcontext['file_count'];
1312
	$upcontext['step_progress'] = 0;
1313
	$upcontext['cur_file_num'] = 0;
1314
	foreach ($files as $file)
1315
	{
1316
		if ($did_not_do)
1317
			$did_not_do--;
1318
		else
1319
		{
1320
			$upcontext['cur_file_num']++;
1321
			$upcontext['cur_file_name'] = $file[0];
1322
			// Do we actually need to do this still?
1323
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1324
			{
1325
				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1326
				if ($nextFile)
1327
				{
1328
					// Only update the version of this if complete.
1329
					$smcFunc['db_insert']('replace',
1330
						$db_prefix . 'settings',
1331
						array('variable' => 'string', 'value' => 'string'),
1332
						array('smfVersion', $file[2]),
1333
						array('variable')
1334
					);
1335
1336
					$modSettings['smfVersion'] = $file[2];
1337
				}
1338
1339
				// If this is XML we only do this stuff once.
1340
				if (isset($_GET['xml']))
1341
				{
1342
					// Flag to move on to the next.
1343
					$upcontext['completed_step'] = true;
1344
					// Did we complete the whole file?
1345
					if ($nextFile)
1346
						$upcontext['current_debug_item_num'] = -1;
1347
					return upgradeExit();
1348
				}
1349
				elseif ($support_js)
1350
					break;
1351
			}
1352
			// Set the progress bar to be right as if we had - even if we hadn't...
1353
			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1354
		}
1355
	}
1356
1357
	$_GET['substep'] = 0;
1358
	// So the template knows we're done.
1359
	if (!$support_js)
1360
	{
1361
		$upcontext['changes_complete'] = true;
1362
1363
		return true;
1364
	}
1365
	return false;
1366
}
1367
1368
1369
// Delete the damn thing!
1370
function DeleteUpgrade()
1371
{
1372
	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...
1373
1374
	// Now it's nice to have some of the basic SMF source files.
1375
	if (!isset($_GET['ssi']) && !$command_line)
1376
		redirectLocation('&ssi=1');
1377
1378
	$upcontext['sub_template'] = 'upgrade_complete';
1379
	$upcontext['page_title'] = 'Upgrade Complete';
1380
1381
	$endl = $command_line ? "\n" : '<br>' . "\n";
1382
1383
	$changes = array(
1384
		'language' => '\'' . (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language) . '\'',
1385
		'db_error_send' => '1',
1386
		'upgradeData' => '\'\'',
1387
	);
1388
1389
	// Are we in maintenance mode?
1390
	if (isset($upcontext['user']['main']))
1391
	{
1392
		if ($command_line)
1393
			echo ' * ';
1394
		$upcontext['removed_maintenance'] = true;
1395
		$changes['maintenance'] = $upcontext['user']['main'];
1396
	}
1397
	// Otherwise if somehow we are in 2 let's go to 1.
1398
	elseif (!empty($maintenance) && $maintenance == 2)
1399
		$changes['maintenance'] = 1;
1400
1401
	// Wipe this out...
1402
	$upcontext['user'] = array();
1403
1404
	require_once($sourcedir . '/Subs-Admin.php');
1405
	updateSettingsFile($changes);
1406
1407
	// Clean any old cache files away.
1408
	upgrade_clean_cache();
1409
1410
	// Can we delete the file?
1411
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1412
1413
	// Now is the perfect time to fetch the SM files.
1414
	if ($command_line)
1415
		cli_scheduled_fetchSMfiles();
1416
	else
1417
	{
1418
		require_once($sourcedir . '/ScheduledTasks.php');
1419
		$forum_version = SMF_VERSION; // The variable is usually defined in index.php so lets just use the constant to do it for us.
1420
		scheduled_fetchSMfiles(); // Now go get those files!
1421
	}
1422
1423
	// Log what we've done.
1424
	if (empty($user_info['id']))
1425
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
1426
1427
	// Log the action manually, so CLI still works.
1428
	$smcFunc['db_insert']('',
1429
		'{db_prefix}log_actions',
1430
		array(
1431
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'inet', 'action' => 'string',
1432
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
1433
		),
1434
		array(
1435
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
1436
			0, 0, 0, json_encode(array('version' => $forum_version, 'member' => $user_info['id'])),
1437
		),
1438
		array('id_action')
1439
	);
1440
	$user_info['id'] = 0;
1441
1442
	// Save the current database version.
1443
	$server_version = $smcFunc['db_server_info']();
1444 View Code Duplication
	if ($db_type == 'mysql' && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51')))
1445
		updateSettings(array('db_mysql_group_by_fix' => '1'));
1446
1447
	if ($command_line)
1448
	{
1449
		echo $endl;
1450
		echo 'Upgrade Complete!', $endl;
1451
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
1452
		exit;
1453
	}
1454
1455
	// Make sure it says we're done.
1456
	$upcontext['overall_percent'] = 100;
1457
	if (isset($upcontext['step_progress']))
1458
		unset($upcontext['step_progress']);
1459
1460
	$_GET['substep'] = 0;
1461
	return false;
1462
}
1463
1464
// Just like the built in one, but setup for CLI to not use themes.
1465
function cli_scheduled_fetchSMfiles()
1466
{
1467
	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...
1468
1469
	if (empty($modSettings['time_format']))
1470
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1471
1472
	// What files do we want to get
1473
	$request = $smcFunc['db_query']('', '
1474
		SELECT id_file, filename, path, parameters
1475
		FROM {db_prefix}admin_info_files',
1476
		array(
1477
		)
1478
	);
1479
1480
	$js_files = array();
1481 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
1482
	{
1483
		$js_files[$row['id_file']] = array(
1484
			'filename' => $row['filename'],
1485
			'path' => $row['path'],
1486
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode($forum_version)),
1487
		);
1488
	}
1489
	$smcFunc['db_free_result']($request);
1490
1491
	// We're gonna need fetch_web_data() to pull this off.
1492
	require_once($sourcedir . '/Subs-Package.php');
1493
1494
	foreach ($js_files as $ID_FILE => $file)
1495
	{
1496
		// Create the url
1497
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'https://www.simplemachines.org' : '';
1498
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1499
1500
		// Get the file
1501
		$file_data = fetch_web_data($url);
1502
1503
		// If we got an error - give up - the site might be down.
1504
		if ($file_data === false)
1505
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
1506
1507
		// Save the file to the database.
1508
		$smcFunc['db_query']('substring', '
1509
			UPDATE {db_prefix}admin_info_files
1510
			SET data = SUBSTRING({string:file_data}, 1, 65534)
1511
			WHERE id_file = {int:id_file}',
1512
			array(
1513
				'id_file' => $ID_FILE,
1514
				'file_data' => $file_data,
1515
			)
1516
		);
1517
	}
1518
	return true;
1519
}
1520
1521
function convertSettingsToTheme()
1522
{
1523
	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...
1524
1525
	$values = array(
1526
		'show_latest_member' => @$GLOBALS['showlatestmember'],
1527
		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
1528
		'show_modify' => @$GLOBALS['showmodify'],
1529
		'show_user_images' => @$GLOBALS['showuserpic'],
1530
		'show_blurb' => @$GLOBALS['showusertext'],
1531
		'show_gender' => @$GLOBALS['showgenderimage'],
1532
		'show_newsfader' => @$GLOBALS['shownewsfader'],
1533
		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
1534
		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
1535
		'linktree_link' => @$GLOBALS['curposlinks'],
1536
		'show_profile_buttons' => @$GLOBALS['profilebutton'],
1537
		'show_mark_read' => @$GLOBALS['showmarkread'],
1538
		'newsfader_time' => @$GLOBALS['fadertime'],
1539
		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
1540
		'enable_news' => @$GLOBALS['enable_news'],
1541
		'return_to_post' => @$modSettings['returnToPost'],
1542
	);
1543
1544
	$themeData = array();
1545
	foreach ($values as $variable => $value)
1546
	{
1547
		if (!isset($value) || $value === null)
1548
			$value = 0;
1549
1550
		$themeData[] = array(0, 1, $variable, $value);
1551
	}
1552 View Code Duplication
	if (!empty($themeData))
1553
	{
1554
		$smcFunc['db_insert']('ignore',
1555
			$db_prefix . 'themes',
1556
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
1557
			$themeData,
1558
			array('id_member', 'id_theme', 'variable')
1559
		);
1560
	}
1561
}
1562
1563
// This function only works with MySQL but that's fine as it is only used for v1.0.
1564
function convertSettingstoOptions()
1565
{
1566
	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...
1567
1568
	// Format: new_setting -> old_setting_name.
1569
	$values = array(
1570
		'calendar_start_day' => 'cal_startmonday',
1571
		'view_newest_first' => 'viewNewestFirst',
1572
		'view_newest_pm_first' => 'viewNewestFirst',
1573
	);
1574
1575
	foreach ($values as $variable => $value)
1576
	{
1577
		if (empty($modSettings[$value[0]]))
1578
			continue;
1579
1580
		$smcFunc['db_query']('', '
1581
			INSERT IGNORE INTO {db_prefix}themes
1582
				(id_member, id_theme, variable, value)
1583
			SELECT id_member, 1, {string:variable}, {string:value}
1584
			FROM {db_prefix}members',
1585
			array(
1586
				'variable' => $variable,
1587
				'value' => $modSettings[$value[0]],
1588
				'db_error_skip' => true,
1589
			)
1590
		);
1591
1592
		$smcFunc['db_query']('', '
1593
			INSERT IGNORE INTO {db_prefix}themes
1594
				(id_member, id_theme, variable, value)
1595
			VALUES (-1, 1, {string:variable}, {string:value})',
1596
			array(
1597
				'variable' => $variable,
1598
				'value' => $modSettings[$value[0]],
1599
				'db_error_skip' => true,
1600
			)
1601
		);
1602
	}
1603
}
1604
1605
function php_version_check()
1606
{
1607
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
1608
}
1609
1610
function db_version_check()
1611
{
1612
	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...
1613
1614
	$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...
1615
	$curver = preg_replace('~\-.+?$~', '', $curver);
1616
1617
	return version_compare($databases[$db_type]['version'], $curver, '<=');
1618
}
1619
1620
function fixRelativePath($path)
1621
{
1622
	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...
1623
1624
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
1625
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
1626
}
1627
1628
function parse_sql($filename)
1629
{
1630
	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...
1631
	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...
1632
1633
/*
1634
	Failure allowed on:
1635
		- INSERT INTO but not INSERT IGNORE INTO.
1636
		- UPDATE IGNORE but not UPDATE.
1637
		- ALTER TABLE and ALTER IGNORE TABLE.
1638
		- DROP TABLE.
1639
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
1640
1641
	If a comment...
1642
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
1643
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
1644
		- is only ---#, it is "done." and then a break - only shown in debug.
1645
		- begins with ---{ it is a code block terminating at ---}.
1646
1647
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
1648
1649
	Replaces the following variables:
1650
		- {$boarddir}
1651
		- {$boardurl}
1652
		- {$db_prefix}
1653
		- {$db_collation}
1654
*/
1655
1656
	// May want to use extended functionality.
1657
	db_extend();
1658
	db_extend('packages');
1659
1660
	// Our custom error handler - does nothing but does stop public errors from XML!
1661
	set_error_handler(
1662
		function ($errno, $errstr, $errfile, $errline) use ($support_js)
1663
		{
1664
			if ($support_js)
1665
				return true;
1666
			else
1667
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
1668
		}
1669
	);
1670
1671
	// If we're on MySQL, set {db_collation}; this approach is used throughout upgrade_2-0_mysql.php to set new tables to utf8
1672
	// Note it is expected to be in the format: ENGINE=MyISAM{$db_collation};
1673
	if ($db_type == 'mysql')
1674
		$db_collation = ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1675
	else
1676
		$db_collation = '';
1677
1678
	$endl = $command_line ? "\n" : '<br>' . "\n";
1679
1680
	$lines = file($filename);
1681
1682
	$current_type = 'sql';
1683
	$current_data = '';
1684
	$substep = 0;
1685
	$last_step = '';
1686
1687
	// Make sure all newly created tables will have the proper characters set; this approach is used throughout upgrade_2-1_mysql.php
1688
	if (isset($db_character_set) && $db_character_set === 'utf8')
1689
		$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
1690
1691
	// Count the total number of steps within this file - for progress.
1692
	$file_steps = substr_count(implode('', $lines), '---#');
1693
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
1694
	$upcontext['debug_items'] = $file_steps;
1695
	$upcontext['current_item_num'] = 0;
1696
	$upcontext['current_item_name'] = '';
1697
	$upcontext['current_debug_item_num'] = 0;
1698
	$upcontext['current_debug_item_name'] = '';
1699
	// This array keeps a record of what we've done in case java is dead...
1700
	$upcontext['actioned_items'] = array();
1701
1702
	$done_something = false;
1703
1704
	foreach ($lines as $line_number => $line)
1705
	{
1706
		$do_current = $substep >= $_GET['substep'];
1707
1708
		// Get rid of any comments in the beginning of the line...
1709
		if (substr(trim($line), 0, 2) === '/*')
1710
			$line = preg_replace('~/\*.+?\*/~', '', $line);
1711
1712
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
1713
		if ($is_debug && !$support_js && $command_line)
1714
			flush();
1715
1716
		if (trim($line) === '')
1717
			continue;
1718
1719
		if (trim(substr($line, 0, 3)) === '---')
1720
		{
1721
			$type = substr($line, 3, 1);
1722
1723
			// An error??
1724
			if (trim($current_data) != '' && $type !== '}')
1725
			{
1726
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
1727
				if ($command_line)
1728
					echo $upcontext['error_message'];
1729
			}
1730
1731
			if ($type == ' ')
1732
			{
1733
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
1734
				{
1735
					echo ' Successful.', $endl;
1736
					flush();
1737
				}
1738
1739
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
1740
				$upcontext['current_item_num']++;
1741
				$upcontext['current_item_name'] = $last_step;
1742
1743
				if ($do_current)
1744
				{
1745
					$upcontext['actioned_items'][] = $last_step;
1746
					if ($command_line)
1747
						echo ' * ';
1748
				}
1749
			}
1750
			elseif ($type == '#')
1751
			{
1752
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
1753
1754
				$upcontext['current_debug_item_num']++;
1755
				if (trim($line) != '---#')
1756
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
1757
1758
				// Have we already done something?
1759
				if (isset($_GET['xml']) && $done_something)
1760
				{
1761
					restore_error_handler();
1762
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
1763
				}
1764
1765
				if ($do_current)
1766
				{
1767
					if (trim($line) == '---#' && $command_line)
1768
						echo ' done.', $endl;
1769
					elseif ($command_line)
1770
						echo ' +++ ', rtrim(substr($line, 4));
1771
					elseif (trim($line) != '---#')
1772
					{
1773
						if ($is_debug)
1774
							$upcontext['actioned_items'][] = htmlspecialchars(rtrim(substr($line, 4)));
1775
					}
1776
				}
1777
1778
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
1779
				{
1780
					if ($command_line)
1781
						echo ' * ';
1782
					else
1783
						$upcontext['actioned_items'][] = $last_step;
1784
				}
1785
1786
				// Small step - only if we're actually doing stuff.
1787
				if ($do_current)
1788
					nextSubstep(++$substep);
1789
				else
1790
					$substep++;
1791
			}
1792
			elseif ($type == '{')
1793
				$current_type = 'code';
1794
			elseif ($type == '}')
1795
			{
1796
				$current_type = 'sql';
1797
1798
				if (!$do_current)
1799
				{
1800
					$current_data = '';
1801
					continue;
1802
				}
1803
1804
				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...
1805
				{
1806
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
1807
					if ($command_line)
1808
						echo $upcontext['error_message'];
1809
				}
1810
1811
				// Done with code!
1812
				$current_data = '';
1813
				$done_something = true;
1814
			}
1815
1816
			continue;
1817
		}
1818
1819
		$current_data .= $line;
1820
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
1821
		{
1822
			if ((!$support_js || isset($_GET['xml'])))
1823
			{
1824
				if (!$do_current)
1825
				{
1826
					$current_data = '';
1827
					continue;
1828
				}
1829
1830
				$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));
1831
1832
				upgrade_query($current_data);
1833
1834
				// @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...
1835
				/*
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...
1836
				$result = $smcFunc['db_query']('', $current_data, false, false);
1837
				// Went wrong?
1838
				if (!$result)
1839
				{
1840
					// Bit of a bodge - do we want the error?
1841
					if (!empty($upcontext['return_error']))
1842
					{
1843
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
1844
						return false;
1845
					}
1846
				}*/
1847
				$done_something = true;
1848
			}
1849
			$current_data = '';
1850
		}
1851
		// If this is xml based and we're just getting the item name then that's grand.
1852
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
1853
		{
1854
			restore_error_handler();
1855
			return false;
1856
		}
1857
1858
		// Clean up by cleaning any step info.
1859
		$step_progress = array();
1860
		$custom_warning = '';
1861
	}
1862
1863
	// Put back the error handler.
1864
	restore_error_handler();
1865
1866
	if ($command_line)
1867
	{
1868
		echo ' Successful.' . "\n";
1869
		flush();
1870
	}
1871
1872
	$_GET['substep'] = 0;
1873
	return true;
1874
}
1875
1876
function upgrade_query($string, $unbuffered = false)
1877
{
1878
	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...
1879
	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...
1880
1881
	// Get the query result - working around some SMF specific security - just this once!
1882
	$modSettings['disableQueryCheck'] = true;
1883
	$db_unbuffered = $unbuffered;
1884
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
1885
	$db_unbuffered = false;
1886
1887
	// Failure?!
1888
	if ($result !== false)
1889
		return $result;
1890
1891
	$db_error_message = $smcFunc['db_error']($db_connection);
1892
	// If MySQL we do something more clever.
1893
	if ($db_type == 'mysql')
1894
	{
1895
		$mysqli_errno = mysqli_errno($db_connection);
1896
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR'));
1897
1898
		// Error numbers:
1899
		//    1016: Can't open file '....MYI'
1900
		//    1050: Table already exists.
1901
		//    1054: Unknown column name.
1902
		//    1060: Duplicate column name.
1903
		//    1061: Duplicate key name.
1904
		//    1062: Duplicate entry for unique key.
1905
		//    1068: Multiple primary keys.
1906
		//    1072: Key column '%s' doesn't exist in table.
1907
		//    1091: Can't drop key, doesn't exist.
1908
		//    1146: Table doesn't exist.
1909
		//    2013: Lost connection to server during query.
1910
1911
		if ($mysqli_errno == 1016)
1912
		{
1913
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
1914
			{
1915
				mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
1916
				$result = mysqli_query($db_connection, $string);
1917
				if ($result !== false)
1918
					return $result;
1919
			}
1920
		}
1921
		elseif ($mysqli_errno == 2013)
1922
		{
1923
			$db_connection = mysqli_connect($db_server, $db_user, $db_passwd);
1924
			mysqli_select_db($db_connection, $db_name);
1925
			if ($db_connection)
1926
			{
1927
				$result = mysqli_query($db_connection, $string);
1928
				if ($result !== false)
1929
					return $result;
1930
			}
1931
		}
1932
		// Duplicate column name... should be okay ;).
1933 View Code Duplication
		elseif (in_array($mysqli_errno, array(1060, 1061, 1068, 1091)))
1934
			return false;
1935
		// Duplicate insert... make sure it's the proper type of query ;).
1936 View Code Duplication
		elseif (in_array($mysqli_errno, array(1054, 1062, 1146)) && $error_query)
1937
			return false;
1938
		// Creating an index on a non-existent column.
1939
		elseif ($mysqli_errno == 1072)
1940
			return false;
1941
		elseif ($mysqli_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
1942
			return false;
1943
	}
1944
	// If a table already exists don't go potty.
1945
	else
1946
	{
1947
		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U')))
1948
		{
1949
			if (strpos($db_error_message, 'exist') !== false)
1950
				return true;
1951
		}
1952
		elseif (strpos(trim($string), 'INSERT ') !== false)
1953
		{
1954
			if (strpos($db_error_message, 'duplicate') !== false)
1955
				return true;
1956
		}
1957
	}
1958
1959
	// Get the query string so we pass everything.
1960
	$query_string = '';
1961
	foreach ($_GET as $k => $v)
1962
		$query_string .= ';' . $k . '=' . $v;
1963
	if (strlen($query_string) != 0)
1964
		$query_string = '?' . substr($query_string, 1);
1965
1966
	if ($command_line)
1967
	{
1968
		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
1969
		die;
1970
	}
1971
1972
	// Bit of a bodge - do we want the error?
1973
	if (!empty($upcontext['return_error']))
1974
	{
1975
		$upcontext['error_message'] = $db_error_message;
1976
		$upcontext['error_string'] = $string;
1977
		return false;
1978
	}
1979
1980
	// Otherwise we have to display this somewhere appropriate if possible.
1981
	$upcontext['forced_error_message'] = '
1982
			<strong>Unsuccessful!</strong><br>
1983
1984
			<div style="margin: 2ex;">
1985
				This query:
1986
				<blockquote><pre>' . nl2br(htmlspecialchars(trim($string))) . ';</pre></blockquote>
1987
1988
				Caused the error:
1989
				<blockquote>' . nl2br(htmlspecialchars($db_error_message)) . '</blockquote>
1990
			</div>
1991
1992
			<form action="' . $upgradeurl . $query_string . '" method="post">
1993
				<input type="submit" value="Try again" class="button">
1994
			</form>
1995
		</div>';
1996
1997
	upgradeExit();
1998
}
1999
2000
// This performs a table alter, but does it unbuffered so the script can time out professionally.
2001
function protected_alter($change, $substep, $is_test = false)
2002
{
2003
	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...
2004
2005
	db_extend('packages');
2006
2007
	// Firstly, check whether the current index/column exists.
2008
	$found = false;
2009
	if ($change['type'] === 'column')
2010
	{
2011
		$columns = $smcFunc['db_list_columns']('{db_prefix}' . $change['table'], true);
2012
		foreach ($columns as $column)
2013
		{
2014
			// Found it?
2015
			if ($column['name'] === $change['name'])
2016
			{
2017
				$found |= 1;
2018
				// Do some checks on the data if we have it set.
2019
				if (isset($change['col_type']))
2020
					$found &= $change['col_type'] === $column['type'];
2021
				if (isset($change['null_allowed']))
2022
					$found &= $column['null'] == $change['null_allowed'];
2023
				if (isset($change['default']))
2024
					$found &= $change['default'] === $column['default'];
2025
			}
2026
		}
2027
	}
2028
	elseif ($change['type'] === 'index')
2029
	{
2030
		$request = upgrade_query('
2031
			SHOW INDEX
2032
			FROM ' . $db_prefix . $change['table']);
2033
		if ($request !== false)
2034
		{
2035
			$cur_index = array();
2036
2037
			while ($row = $smcFunc['db_fetch_assoc']($request))
2038
				if ($row['Key_name'] === $change['name'])
2039
					$cur_index[(int) $row['Seq_in_index']] = $row['Column_name'];
2040
2041
			ksort($cur_index, SORT_NUMERIC);
2042
			$found = array_values($cur_index) === $change['target_columns'];
2043
2044
			$smcFunc['db_free_result']($request);
2045
		}
2046
	}
2047
2048
	// If we're trying to add and it's added, we're done.
2049
	if ($found && in_array($change['method'], array('add', 'change')))
2050
		return true;
2051
	// Otherwise if we're removing and it wasn't found we're also done.
2052
	elseif (!$found && in_array($change['method'], array('remove', 'change_remove')))
2053
		return true;
2054
	// Otherwise is it just a test?
2055
	elseif ($is_test)
2056
		return false;
2057
2058
	// Not found it yet? Bummer! How about we see if we're currently doing it?
2059
	$running = false;
2060
	$found = false;
2061
	while (1 == 1)
2062
	{
2063
		$request = upgrade_query('
2064
			SHOW FULL PROCESSLIST');
2065
		while ($row = $smcFunc['db_fetch_assoc']($request))
2066
		{
2067
			if (strpos($row['Info'], 'ALTER TABLE ' . $db_prefix . $change['table']) !== false && strpos($row['Info'], $change['text']) !== false)
2068
				$found = true;
2069
		}
2070
2071
		// Can't find it? Then we need to run it fools!
2072
		if (!$found && !$running)
2073
		{
2074
			$smcFunc['db_free_result']($request);
2075
2076
			$success = upgrade_query('
2077
				ALTER TABLE ' . $db_prefix . $change['table'] . '
2078
				' . $change['text'], true) !== false;
2079
2080
			if (!$success)
2081
				return false;
2082
2083
			// Return
2084
			$running = true;
2085
		}
2086
		// What if we've not found it, but we'd ran it already? Must of completed.
2087
		elseif (!$found)
2088
		{
2089
			$smcFunc['db_free_result']($request);
2090
			return true;
2091
		}
2092
2093
		// Pause execution for a sec or three.
2094
		sleep(3);
2095
2096
		// Can never be too well protected.
2097
		nextSubstep($substep);
2098
	}
2099
2100
	// Protect it.
2101
	nextSubstep($substep);
2102
}
2103
2104
/**
2105
 * Alter a text column definition preserving its character set.
2106
 *
2107
 * @param array $change
2108
 * @param int $substep
2109
 */
2110
function textfield_alter($change, $substep)
2111
{
2112
	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...
2113
2114
	$request = $smcFunc['db_query']('', '
2115
		SHOW FULL COLUMNS
2116
		FROM {db_prefix}' . $change['table'] . '
2117
		LIKE {string:column}',
2118
		array(
2119
			'column' => $change['column'],
2120
			'db_error_skip' => true,
2121
		)
2122
	);
2123
	if ($smcFunc['db_num_rows']($request) === 0)
2124
		die('Unable to find column ' . $change['column'] . ' inside table ' . $db_prefix . $change['table']);
2125
	$table_row = $smcFunc['db_fetch_assoc']($request);
2126
	$smcFunc['db_free_result']($request);
2127
2128
	// If something of the current column definition is different, fix it.
2129
	$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']);
2130
2131
	// Columns that previously allowed null, need to be converted first.
2132
	$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
2133
2134
	// Get the character set that goes with the collation of the column.
2135 View Code Duplication
	if ($column_fix && !empty($table_row['Collation']))
2136
	{
2137
		$request = $smcFunc['db_query']('', '
2138
			SHOW COLLATION
2139
			LIKE {string:collation}',
2140
			array(
2141
				'collation' => $table_row['Collation'],
2142
				'db_error_skip' => true,
2143
			)
2144
		);
2145
		// No results? Just forget it all together.
2146
		if ($smcFunc['db_num_rows']($request) === 0)
2147
			unset($table_row['Collation']);
2148
		else
2149
			$collation_info = $smcFunc['db_fetch_assoc']($request);
2150
		$smcFunc['db_free_result']($request);
2151
	}
2152
2153
	if ($column_fix)
2154
	{
2155
		// Make sure there are no NULL's left.
2156
		if ($null_fix)
2157
			$smcFunc['db_query']('', '
2158
				UPDATE {db_prefix}' . $change['table'] . '
2159
				SET ' . $change['column'] . ' = {string:default}
2160
				WHERE ' . $change['column'] . ' IS NULL',
2161
				array(
2162
					'default' => isset($change['default']) ? $change['default'] : '',
2163
					'db_error_skip' => true,
2164
				)
2165
			);
2166
2167
		// Do the actual alteration.
2168
		$smcFunc['db_query']('', '
2169
			ALTER TABLE {db_prefix}' . $change['table'] . '
2170
			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}' : ''),
2171
			array(
2172
				'default' => isset($change['default']) ? $change['default'] : '',
2173
				'db_error_skip' => true,
2174
			)
2175
		);
2176
	}
2177
	nextSubstep($substep);
2178
}
2179
2180
// Check if we need to alter this query.
2181
function checkChange(&$change)
2182
{
2183
	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...
2184
	static $database_version, $where_field_support;
2185
2186
	// Attempt to find a database_version.
2187
	if (empty($database_version))
2188
	{
2189
		$database_version = $databases[$db_type]['version_check'];
2190
		$where_field_support = $db_type == 'mysql' && version_compare('5.0', $database_version, '<=');
2191
	}
2192
2193
	// Not a column we need to check on?
2194
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
2195
		return;
2196
2197
	// Break it up you (six|seven).
2198
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
2199
2200
	// Can we support a shortcut method?
2201
	if ($where_field_support)
2202
	{
2203
		// Get the details about this change.
2204
		$request = $smcFunc['db_query']('', '
2205
			SHOW FIELDS
2206
			FROM {db_prefix}{raw:table}
2207
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
2208
			array(
2209
				'table' => $change['table'],
2210
				'old_name' => $temp[1],
2211
				'new_name' => $temp[2],
2212
		));
2213
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2214
		if ($smcFunc['db_num_rows'] != 1)
2215
			return;
2216
2217
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
2218
		$smcFunc['db_free_result']($request);
2219
	}
2220
	else
2221
	{
2222
		// Do this the old fashion, sure method way.
2223
		$request = $smcFunc['db_query']('', '
2224
			SHOW FIELDS
2225
			FROM {db_prefix}{raw:table}',
2226
			array(
2227
				'table' => $change['table'],
2228
		));
2229
		// Mayday!
2230
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2231
		if ($smcFunc['db_num_rows'] == 0)
2232
			return;
2233
2234
		// Oh where, oh where has my little field gone. Oh where can it be...
2235
		while ($row = $smcFunc['db_query']($request))
2236
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
2237
			{
2238
				$current_type = $row['Type'];
2239
				break;
2240
			}
2241
	}
2242
2243
	// If this doesn't match, the column may of been altered for a reason.
2244
	if (trim($current_type) != trim($temp[3]))
2245
		$temp[3] = $current_type;
0 ignored issues
show
Bug introduced by
The variable $current_type does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2246
2247
	// Piece this back together.
2248
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
2249
}
2250
2251
// The next substep.
2252
function nextSubstep($substep)
2253
{
2254
	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...
2255
	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...
2256
2257
	if ($_GET['substep'] < $substep)
2258
		$_GET['substep'] = $substep;
2259
2260
	if ($command_line)
2261
	{
2262
		if (time() - $start_time > 1 && empty($is_debug))
2263
		{
2264
			echo '.';
2265
			$start_time = time();
2266
		}
2267
		return;
2268
	}
2269
2270
	@set_time_limit(300);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

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

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2273
2274
	if (time() - $start_time <= $timeLimitThreshold)
2275
		return;
2276
2277
	// Do we have some custom step progress stuff?
2278
	if (!empty($step_progress))
2279
	{
2280
		$upcontext['substep_progress'] = 0;
2281
		$upcontext['substep_progress_name'] = $step_progress['name'];
2282
		if ($step_progress['current'] > $step_progress['total'])
2283
			$upcontext['substep_progress'] = 99.9;
2284
		else
2285
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2286
2287
		// Make it nicely rounded.
2288
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2289
	}
2290
2291
	// If this is XML we just exit right away!
2292
	if (isset($_GET['xml']))
2293
		return upgradeExit();
2294
2295
	// We're going to pause after this!
2296
	$upcontext['pause'] = true;
2297
2298
	$upcontext['query_string'] = '';
2299
	foreach ($_GET as $k => $v)
2300
	{
2301
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2302
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2303
	}
2304
2305
	// Custom warning?
2306
	if (!empty($custom_warning))
2307
		$upcontext['custom_warning'] = $custom_warning;
2308
2309
	upgradeExit();
2310
}
2311
2312
function cmdStep0()
2313
{
2314
	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...
2315
	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...
2316
	$start_time = time();
2317
2318
	ob_end_clean();
2319
	ob_implicit_flush(true);
2320
	@set_time_limit(600);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2321
2322
	if (!isset($_SERVER['argv']))
2323
		$_SERVER['argv'] = array();
2324
	$_GET['maint'] = 1;
2325
2326
	foreach ($_SERVER['argv'] as $i => $arg)
2327
	{
2328
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2329
			$_GET['lang'] = $match[1];
2330
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2331
			continue;
2332
		elseif ($arg == '--no-maintenance')
2333
			$_GET['maint'] = 0;
2334
		elseif ($arg == '--debug')
2335
			$is_debug = true;
2336
		elseif ($arg == '--backup')
2337
			$_POST['backup'] = 1;
2338
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2339
			$_GET['conv'] = 1;
2340
		elseif ($i != 0)
2341
		{
2342
			echo 'SMF Command-line Upgrader
2343
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2344
2345
    --language=LANG         Reset the forum\'s language to LANG.
2346
    --no-maintenance        Don\'t put the forum into maintenance mode.
2347
    --debug                 Output debugging information.
2348
    --backup                Create backups of tables with "backup_" prefix.';
2349
			echo "\n";
2350
			exit;
2351
		}
2352
	}
2353
2354
	if (!php_version_check())
2355
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
2356
	if (!db_version_check())
2357
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2358
2359
	// Do some checks to make sure they have proper privileges
2360
	db_extend('packages');
2361
2362
	// CREATE
2363
	$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');
2364
2365
	// ALTER
2366
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'tinytext', 'null' => false, 'default' => ''));
2367
2368
	// DROP
2369
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2370
2371
	// Sorry... we need CREATE, ALTER and DROP
2372 View Code Duplication
	if (!$create || !$alter || !$drop)
2373
		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...
2374
2375
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2376
		&& @file_exists($sourcedir . '/QueryString.php')
2377
		&& @file_exists($sourcedir . '/ManageBoards.php');
2378
	if (!$check && !isset($modSettings['smfVersion']))
2379
		print_error('Error: Some files are missing or out-of-date.', true);
2380
2381
	// Do a quick version spot check.
2382
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
2383
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2384
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2385
		print_error('Error: Some files have not yet been updated properly.');
2386
2387
	// Make sure Settings.php is writable.
2388
	quickFileWritable($boarddir . '/Settings.php');
2389
	if (!is_writable($boarddir . '/Settings.php'))
2390
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2391
2392
	// Make sure Settings_bak.php is writable.
2393
	quickFileWritable($boarddir . '/Settings_bak.php');
2394
	if (!is_writable($boarddir . '/Settings_bak.php'))
2395
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2396
2397
	// Make sure db_last_error.php is writable.
2398
	quickFileWritable($boarddir . '/db_last_error.php');
2399
	if (!is_writable($boarddir . '/db_last_error.php'))
2400
		print_error('Error: Unable to obtain write access to "db_last_error.php".');
2401
2402 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
2403
		print_error('Error: Unable to obtain write access to "agreement.txt".');
2404
	elseif (isset($modSettings['agreement']))
2405
	{
2406
		$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...
2407
		fwrite($fp, $modSettings['agreement']);
2408
		fclose($fp);
2409
	}
2410
2411
	// Make sure Themes is writable.
2412
	quickFileWritable($modSettings['theme_dir']);
2413
2414
	if (!is_writable($modSettings['theme_dir']) && !isset($modSettings['smfVersion']))
2415
		print_error('Error: Unable to obtain write access to "Themes".');
2416
2417
	// Make sure cache directory exists and is writable!
2418
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
2419
	if (!file_exists($cachedir_temp))
2420
		@mkdir($cachedir_temp);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2421
2422
	// Make sure the cache temp dir is writable.
2423
	quickFileWritable($cachedir_temp);
2424
2425
	if (!is_writable($cachedir_temp))
2426
		print_error('Error: Unable to obtain write access to "cache".', true);
2427
2428
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
2429
		print_error('Error: Unable to find language files!', true);
2430
	else
2431
	{
2432
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
2433
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
2434
2435
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
2436
			print_error('Error: Language files out of date.', true);
2437
		if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
2438
			print_error('Error: Install language is missing for selected language.', true);
2439
2440
		// Otherwise include it!
2441
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
2442
	}
2443
2444
	// Make sure we skip the HTML for login.
2445
	$_POST['upcont'] = true;
2446
	$upcontext['current_step'] = 1;
2447
}
2448
2449
/**
2450
 * Handles converting your database to UTF-8
2451
 */
2452
function ConvertUtf8()
2453
{
2454
	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...
2455
2456
	// Done it already?
2457
	if (!empty($_POST['utf8_done']))
2458
		return true;
2459
2460
	// First make sure they aren't already on UTF-8 before we go anywhere...
2461
	if ($db_type == 'postgresql' || ($db_character_set === 'utf8' && !empty($modSettings['global_character_set']) && $modSettings['global_character_set'] === 'UTF-8'))
2462
	{
2463
		$smcFunc['db_insert']('replace',
2464
			'{db_prefix}settings',
2465
			array('variable' => 'string', 'value' => 'string'),
2466
			array(array('global_character_set', 'UTF-8')),
2467
			array('variable')
2468
		);
2469
2470
		return true;
2471
	}
2472
	else
2473
	{
2474
		$upcontext['page_title'] = 'Converting to UTF8';
2475
		$upcontext['sub_template'] = isset($_GET['xml']) ? 'convert_xml' : 'convert_utf8';
2476
2477
		// The character sets used in SMF's language files with their db equivalent.
2478
		$charsets = array(
2479
			// Armenian
2480
			'armscii8' => 'armscii8',
2481
			// Chinese-traditional.
2482
			'big5' => 'big5',
2483
			// Chinese-simplified.
2484
			'gbk' => 'gbk',
2485
			// West European.
2486
			'ISO-8859-1' => 'latin1',
2487
			// Romanian.
2488
			'ISO-8859-2' => 'latin2',
2489
			// Turkish.
2490
			'ISO-8859-9' => 'latin5',
2491
			// Latvian
2492
			'ISO-8859-13' => 'latin7',
2493
			// West European with Euro sign.
2494
			'ISO-8859-15' => 'latin9',
2495
			// Thai.
2496
			'tis-620' => 'tis620',
2497
			// Persian, Chinese, etc.
2498
			'UTF-8' => 'utf8',
2499
			// Russian.
2500
			'windows-1251' => 'cp1251',
2501
			// Greek.
2502
			'windows-1253' => 'utf8',
2503
			// Hebrew.
2504
			'windows-1255' => 'utf8',
2505
			// Arabic.
2506
			'windows-1256' => 'cp1256',
2507
		);
2508
2509
		// Get a list of character sets supported by your MySQL server.
2510
		$request = $smcFunc['db_query']('', '
2511
			SHOW CHARACTER SET',
2512
			array(
2513
			)
2514
		);
2515
		$db_charsets = array();
2516
		while ($row = $smcFunc['db_fetch_assoc']($request))
2517
			$db_charsets[] = $row['Charset'];
2518
2519
		$smcFunc['db_free_result']($request);
2520
2521
		// Character sets supported by both MySQL and SMF's language files.
2522
		$charsets = array_intersect($charsets, $db_charsets);
2523
2524
		// Use the messages.body column as indicator for the database charset.
2525
		$request = $smcFunc['db_query']('', '
2526
			SHOW FULL COLUMNS
2527
			FROM {db_prefix}messages
2528
			LIKE {string:body_like}',
2529
			array(
2530
				'body_like' => 'body',
2531
			)
2532
		);
2533
		$column_info = $smcFunc['db_fetch_assoc']($request);
2534
		$smcFunc['db_free_result']($request);
2535
2536
		// A collation looks like latin1_swedish. We only need the character set.
2537
		list($upcontext['database_charset']) = explode('_', $column_info['Collation']);
2538
		$upcontext['database_charset'] = in_array($upcontext['database_charset'], $charsets) ? array_search($upcontext['database_charset'], $charsets) : $upcontext['database_charset'];
2539
2540
		// Detect whether a fulltext index is set.
2541
		$request = $smcFunc['db_query']('', '
2542
 			SHOW INDEX
2543
	  	    FROM {db_prefix}messages',
2544
			array(
2545
			)
2546
		);
2547
2548
		$upcontext['dropping_index'] = false;
2549
2550
		// If there's a fulltext index, we need to drop it first...
2551 View Code Duplication
		if ($request !== false || $smcFunc['db_num_rows']($request) != 0)
2552
		{
2553
			while ($row = $smcFunc['db_fetch_assoc']($request))
2554
				if ($row['Column_name'] == 'body' && (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT' || isset($row['Comment']) && $row['Comment'] == 'FULLTEXT'))
2555
					$upcontext['fulltext_index'][] = $row['Key_name'];
2556
			$smcFunc['db_free_result']($request);
2557
2558
			if (isset($upcontext['fulltext_index']))
2559
				$upcontext['fulltext_index'] = array_unique($upcontext['fulltext_index']);
2560
		}
2561
2562
		// Drop it and make a note...
2563
		if (!empty($upcontext['fulltext_index']))
2564
		{
2565
			$upcontext['dropping_index'] = true;
2566
2567
			$smcFunc['db_query']('', '
2568
  			ALTER TABLE {db_prefix}messages
2569
	  		DROP INDEX ' . implode(',
2570
		  	DROP INDEX ', $upcontext['fulltext_index']),
2571
				array(
2572
					'db_error_skip' => true,
2573
				)
2574
			);
2575
2576
			// Update the settings table
2577
			$smcFunc['db_insert']('replace',
2578
				'{db_prefix}settings',
2579
				array('variable' => 'string', 'value' => 'string'),
2580
				array('db_search_index', ''),
2581
				array('variable')
2582
			);
2583
		}
2584
2585
		// Figure out what charset we should be converting from...
2586
		$lang_charsets = array(
2587
			'arabic' => 'windows-1256',
2588
			'armenian_east' => 'armscii-8',
2589
			'armenian_west' => 'armscii-8',
2590
			'azerbaijani_latin' => 'ISO-8859-9',
2591
			'bangla' => 'UTF-8',
2592
			'belarusian' => 'ISO-8859-5',
2593
			'bulgarian' => 'windows-1251',
2594
			'cambodian' => 'UTF-8',
2595
			'chinese_simplified' => 'gbk',
2596
			'chinese_traditional' => 'big5',
2597
			'croation' => 'ISO-8859-2',
2598
			'czech' => 'ISO-8859-2',
2599
			'czech_informal' => 'ISO-8859-2',
2600
			'english_pirate' => 'UTF-8',
2601
			'esperanto' => 'ISO-8859-3',
2602
			'estonian' => 'ISO-8859-15',
2603
			'filipino_tagalog' => 'UTF-8',
2604
			'filipino_vasayan' => 'UTF-8',
2605
			'georgian' => 'UTF-8',
2606
			'greek' => 'ISO-8859-3',
2607
			'hebrew' => 'windows-1255',
2608
			'hungarian' => 'ISO-8859-2',
2609
			'irish' => 'UTF-8',
2610
			'japanese' => 'UTF-8',
2611
			'khmer' => 'UTF-8',
2612
			'korean' => 'UTF-8',
2613
			'kurdish_kurmanji' => 'ISO-8859-9',
2614
			'kurdish_sorani' => 'windows-1256',
2615
			'lao' => 'tis-620',
2616
			'latvian' => 'ISO-8859-13',
2617
			'lithuanian' => 'ISO-8859-4',
2618
			'macedonian' => 'UTF-8',
2619
			'malayalam' => 'UTF-8',
2620
			'mongolian' => 'UTF-8',
2621
			'nepali' => 'UTF-8',
2622
			'persian' => 'UTF-8',
2623
			'polish' => 'ISO-8859-2',
2624
			'romanian' => 'ISO-8859-2',
2625
			'russian' => 'windows-1252',
2626
			'sakha' => 'UTF-8',
2627
			'serbian_cyrillic' => 'ISO-8859-5',
2628
			'serbian_latin' => 'ISO-8859-2',
2629
			'sinhala' => 'UTF-8',
2630
			'slovak' => 'ISO-8859-2',
2631
			'slovenian' => 'ISO-8859-2',
2632
			'telugu' => 'UTF-8',
2633
			'thai' => 'tis-620',
2634
			'turkish' => 'ISO-8859-9',
2635
			'turkmen' => 'ISO-8859-9',
2636
			'ukranian' => 'windows-1251',
2637
			'urdu' => 'UTF-8',
2638
			'uzbek_cyrillic' => 'ISO-8859-5',
2639
			'uzbek_latin' => 'ISO-8859-5',
2640
			'vietnamese' => 'UTF-8',
2641
			'yoruba' => 'UTF-8'
2642
		);
2643
2644
		// Default to ISO-8859-1 unless we detected another supported charset
2645
		$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';
2646
2647
		$upcontext['charset_list'] = array_keys($charsets);
2648
2649
		// Translation table for the character sets not native for MySQL.
2650
		$translation_tables = array(
2651
			'windows-1255' => array(
2652
				'0x81' => '\'\'',		'0x8A' => '\'\'',		'0x8C' => '\'\'',
2653
				'0x8D' => '\'\'',		'0x8E' => '\'\'',		'0x8F' => '\'\'',
2654
				'0x90' => '\'\'',		'0x9A' => '\'\'',		'0x9C' => '\'\'',
2655
				'0x9D' => '\'\'',		'0x9E' => '\'\'',		'0x9F' => '\'\'',
2656
				'0xCA' => '\'\'',		'0xD9' => '\'\'',		'0xDA' => '\'\'',
2657
				'0xDB' => '\'\'',		'0xDC' => '\'\'',		'0xDD' => '\'\'',
2658
				'0xDE' => '\'\'',		'0xDF' => '\'\'',		'0xFB' => '0xD792',
2659
				'0xFC' => '0xE282AC',		'0xFF' => '0xD6B2',		'0xC2' => '0xFF',
2660
				'0x80' => '0xFC',		'0xE2' => '0xFB',		'0xA0' => '0xC2A0',
2661
				'0xA1' => '0xC2A1',		'0xA2' => '0xC2A2',		'0xA3' => '0xC2A3',
2662
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2663
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2664
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2665
				'0xAF' => '0xC2AF',		'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',
2666
				'0xB2' => '0xC2B2',		'0xB3' => '0xC2B3',		'0xB4' => '0xC2B4',
2667
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2668
				'0xB8' => '0xC2B8',		'0xB9' => '0xC2B9',		'0xBB' => '0xC2BB',
2669
				'0xBC' => '0xC2BC',		'0xBD' => '0xC2BD',		'0xBE' => '0xC2BE',
2670
				'0xBF' => '0xC2BF',		'0xD7' => '0xD7B3',		'0xD1' => '0xD781',
2671
				'0xD4' => '0xD7B0',		'0xD5' => '0xD7B1',		'0xD6' => '0xD7B2',
2672
				'0xE0' => '0xD790',		'0xEA' => '0xD79A',		'0xEC' => '0xD79C',
2673
				'0xED' => '0xD79D',		'0xEE' => '0xD79E',		'0xEF' => '0xD79F',
2674
				'0xF0' => '0xD7A0',		'0xF1' => '0xD7A1',		'0xF2' => '0xD7A2',
2675
				'0xF3' => '0xD7A3',		'0xF5' => '0xD7A5',		'0xF6' => '0xD7A6',
2676
				'0xF7' => '0xD7A7',		'0xF8' => '0xD7A8',		'0xF9' => '0xD7A9',
2677
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2678
				'0x86' => '0xE280A0',	'0x87' => '0xE280A1',	'0x89' => '0xE280B0',
2679
				'0x8B' => '0xE280B9',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2680
				'0x95' => '0xE280A2',	'0x97' => '0xE28094',	'0x99' => '0xE284A2',
2681
				'0xC0' => '0xD6B0',		'0xC1' => '0xD6B1',		'0xC3' => '0xD6B3',
2682
				'0xC4' => '0xD6B4',		'0xC5' => '0xD6B5',		'0xC6' => '0xD6B6',
2683
				'0xC7' => '0xD6B7',		'0xC8' => '0xD6B8',		'0xC9' => '0xD6B9',
2684
				'0xCB' => '0xD6BB',		'0xCC' => '0xD6BC',		'0xCD' => '0xD6BD',
2685
				'0xCE' => '0xD6BE',		'0xCF' => '0xD6BF',		'0xD0' => '0xD780',
2686
				'0xD2' => '0xD782',		'0xE3' => '0xD793',		'0xE4' => '0xD794',
2687
				'0xE5' => '0xD795',		'0xE7' => '0xD797',		'0xE9' => '0xD799',
2688
				'0xFD' => '0xE2808E',	'0xFE' => '0xE2808F',	'0x92' => '0xE28099',
2689
				'0x83' => '0xC692',		'0xD3' => '0xD783',		'0x88' => '0xCB86',
2690
				'0x98' => '0xCB9C',		'0x91' => '0xE28098',	'0x96' => '0xE28093',
2691
				'0xBA' => '0xC3B7',		'0x9B' => '0xE280BA',	'0xAA' => '0xC397',
2692
				'0xA4' => '0xE282AA',	'0xE1' => '0xD791',		'0xE6' => '0xD796',
2693
				'0xE8' => '0xD798',		'0xEB' => '0xD79B',		'0xF4' => '0xD7A4',
2694
				'0xFA' => '0xD7AA',
2695
			),
2696
			'windows-1253' => array(
2697
				'0x81' => '\'\'',			'0x88' => '\'\'',			'0x8A' => '\'\'',
2698
				'0x8C' => '\'\'',			'0x8D' => '\'\'',			'0x8E' => '\'\'',
2699
				'0x8F' => '\'\'',			'0x90' => '\'\'',			'0x98' => '\'\'',
2700
				'0x9A' => '\'\'',			'0x9C' => '\'\'',			'0x9D' => '\'\'',
2701
				'0x9E' => '\'\'',			'0x9F' => '\'\'',			'0xAA' => '\'\'',
2702
				'0xD2' => '0xE282AC',			'0xFF' => '0xCE92',			'0xCE' => '0xCE9E',
2703
				'0xB8' => '0xCE88',		'0xBA' => '0xCE8A',		'0xBC' => '0xCE8C',
2704
				'0xBE' => '0xCE8E',		'0xBF' => '0xCE8F',		'0xC0' => '0xCE90',
2705
				'0xC8' => '0xCE98',		'0xCA' => '0xCE9A',		'0xCC' => '0xCE9C',
2706
				'0xCD' => '0xCE9D',		'0xCF' => '0xCE9F',		'0xDA' => '0xCEAA',
2707
				'0xE8' => '0xCEB8',		'0xEA' => '0xCEBA',		'0xEC' => '0xCEBC',
2708
				'0xEE' => '0xCEBE',		'0xEF' => '0xCEBF',		'0xC2' => '0xFF',
2709
				'0xBD' => '0xC2BD',		'0xED' => '0xCEBD',		'0xB2' => '0xC2B2',
2710
				'0xA0' => '0xC2A0',		'0xA3' => '0xC2A3',		'0xA4' => '0xC2A4',
2711
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2712
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2713
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2714
				'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',		'0xB3' => '0xC2B3',
2715
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2716
				'0xBB' => '0xC2BB',		'0xE2' => '0xCEB2',		'0x80' => '0xD2',
2717
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2718
				'0x86' => '0xE280A0',	'0xA1' => '0xCE85',		'0xA2' => '0xCE86',
2719
				'0x87' => '0xE280A1',	'0x89' => '0xE280B0',	'0xB9' => '0xCE89',
2720
				'0x8B' => '0xE280B9',	'0x91' => '0xE28098',	'0x99' => '0xE284A2',
2721
				'0x92' => '0xE28099',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2722
				'0x95' => '0xE280A2',	'0x96' => '0xE28093',	'0x97' => '0xE28094',
2723
				'0x9B' => '0xE280BA',	'0xAF' => '0xE28095',	'0xB4' => '0xCE84',
2724
				'0xC1' => '0xCE91',		'0xC3' => '0xCE93',		'0xC4' => '0xCE94',
2725
				'0xC5' => '0xCE95',		'0xC6' => '0xCE96',		'0x83' => '0xC692',
2726
				'0xC7' => '0xCE97',		'0xC9' => '0xCE99',		'0xCB' => '0xCE9B',
2727
				'0xD0' => '0xCEA0',		'0xD1' => '0xCEA1',		'0xD3' => '0xCEA3',
2728
				'0xD4' => '0xCEA4',		'0xD5' => '0xCEA5',		'0xD6' => '0xCEA6',
2729
				'0xD7' => '0xCEA7',		'0xD8' => '0xCEA8',		'0xD9' => '0xCEA9',
2730
				'0xDB' => '0xCEAB',		'0xDC' => '0xCEAC',		'0xDD' => '0xCEAD',
2731
				'0xDE' => '0xCEAE',		'0xDF' => '0xCEAF',		'0xE0' => '0xCEB0',
2732
				'0xE1' => '0xCEB1',		'0xE3' => '0xCEB3',		'0xE4' => '0xCEB4',
2733
				'0xE5' => '0xCEB5',		'0xE6' => '0xCEB6',		'0xE7' => '0xCEB7',
2734
				'0xE9' => '0xCEB9',		'0xEB' => '0xCEBB',		'0xF0' => '0xCF80',
2735
				'0xF1' => '0xCF81',		'0xF2' => '0xCF82',		'0xF3' => '0xCF83',
2736
				'0xF4' => '0xCF84',		'0xF5' => '0xCF85',		'0xF6' => '0xCF86',
2737
				'0xF7' => '0xCF87',		'0xF8' => '0xCF88',		'0xF9' => '0xCF89',
2738
				'0xFA' => '0xCF8A',		'0xFB' => '0xCF8B',		'0xFC' => '0xCF8C',
2739
				'0xFD' => '0xCF8D',		'0xFE' => '0xCF8E',
2740
			),
2741
		);
2742
2743
		// Make some preparations.
2744
		if (isset($translation_tables[$upcontext['charset_detected']]))
2745
		{
2746
			$replace = '%field%';
2747
2748
			// Build a huge REPLACE statement...
2749
			foreach ($translation_tables[$upcontext['charset_detected']] as $from => $to)
2750
				$replace = 'REPLACE(' . $replace . ', ' . $from . ', ' . $to . ')';
2751
		}
2752
2753
		// Get a list of table names ahead of time... This makes it easier to set our substep and such
2754
		db_extend();
2755
		$queryTables = $smcFunc['db_list_tables'](false, $db_prefix . '%');
2756
2757
		$upcontext['table_count'] = count($queryTables);
2758
2759
		// What ones have we already done?
2760
		foreach ($queryTables as $id => $table)
2761
			if ($id < $_GET['substep'])
2762
				$upcontext['previous_tables'][] = $table;
2763
2764
		$upcontext['cur_table_num'] = $_GET['substep'];
2765
		$upcontext['cur_table_name'] = str_replace($db_prefix, '', $queryTables[$_GET['substep']]);
2766
		$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2767
2768
		// Make sure we're ready & have painted the template before proceeding
2769
		if ($support_js && !isset($_GET['xml'])) {
2770
			$_GET['substep'] = 0;
2771
			return false;
2772
		}
2773
2774
		// We want to start at the first table.
2775
		for ($substep = $_GET['substep'], $n = count($queryTables); $substep < $n; $substep++)
2776
		{
2777
			$table = $queryTables[$substep];
2778
2779
			$getTableStatus = $smcFunc['db_query']('', '
2780
				SHOW TABLE STATUS
2781
				LIKE {string:table_name}',
2782
				array(
2783
					'table_name' => str_replace('_', '\_', $table)
2784
				)
2785
			);
2786
2787
			// Only one row so we can just fetch_assoc and free the result...
2788
			$table_info = $smcFunc['db_fetch_assoc']($getTableStatus);
2789
			$smcFunc['db_free_result']($getTableStatus);
2790
2791
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($queryTables[$substep + 1]) ? $queryTables[$substep + 1] : $queryTables[$substep]));
2792
			$upcontext['cur_table_num'] = $substep + 1;
2793
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2794
2795
			// Do we need to pause?
2796
			nextSubstep($substep);
2797
2798
			// Just to make sure it doesn't time out.
2799
			if (function_exists('apache_reset_timeout'))
2800
				@apache_reset_timeout();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2801
2802
			$table_charsets = array();
2803
2804
			// Loop through each column.
2805
			$queryColumns = $smcFunc['db_query']('', '
2806
				SHOW FULL COLUMNS
2807
				FROM ' . $table_info['Name'],
2808
				array(
2809
				)
2810
			);
2811
			while ($column_info = $smcFunc['db_fetch_assoc']($queryColumns))
2812
			{
2813
				// Only text'ish columns have a character set and need converting.
2814
				if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false)
2815
				{
2816
					$collation = empty($column_info['Collation']) || $column_info['Collation'] === 'NULL' ? $table_info['Collation'] : $column_info['Collation'];
2817
					if (!empty($collation) && $collation !== 'NULL')
2818
					{
2819
						list($charset) = explode('_', $collation);
2820
2821
						// Build structure of columns to operate on organized by charset; only operate on columns not yet utf8
2822
						if ($charset != 'utf8') {
2823
							if (!isset($table_charsets[$charset]))
2824
								$table_charsets[$charset] = array();
2825
2826
							$table_charsets[$charset][] = $column_info;
2827
						}
2828
					}
2829
				}
2830
			}
2831
			$smcFunc['db_free_result']($queryColumns);
2832
2833
			// Only change the non-utf8 columns identified above
2834
			if (count($table_charsets) > 0)
2835
			{
2836
				$updates_blob = '';
2837
				$updates_text = '';
2838
				foreach ($table_charsets as $charset => $columns)
2839
				{
2840
					if ($charset !== $charsets[$upcontext['charset_detected']])
2841
					{
2842
						foreach ($columns as $column)
2843
						{
2844
							$updates_blob .= '
2845
								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'] . '\'') . ',';
2846
							$updates_text .= '
2847
								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'] . '\'') . ',';
2848
						}
2849
					}
2850
				}
2851
2852
				// Change the columns to binary form.
2853
				$smcFunc['db_query']('', '
2854
					ALTER TABLE {raw:table_name}{raw:updates_blob}',
2855
					array(
2856
						'table_name' => $table_info['Name'],
2857
						'updates_blob' => substr($updates_blob, 0, -1),
2858
					)
2859
				);
2860
2861
				// Convert the character set if MySQL has no native support for it.
2862
				if (isset($translation_tables[$upcontext['charset_detected']]))
2863
				{
2864
					$update = '';
2865
					foreach ($table_charsets as $charset => $columns)
2866
						foreach ($columns as $column)
2867
							$update .= '
2868
								' . $column['Field'] . ' = ' . strtr($replace, array('%field%' => $column['Field'])) . ',';
0 ignored issues
show
Bug introduced by
The variable $replace does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2869
2870
					$smcFunc['db_query']('', '
2871
						UPDATE {raw:table_name}
2872
						SET {raw:updates}',
2873
						array(
2874
							'table_name' => $table_info['Name'],
2875
							'updates' => substr($update, 0, -1),
2876
						)
2877
					);
2878
				}
2879
2880
				// Change the columns back, but with the proper character set.
2881
				$smcFunc['db_query']('', '
2882
					ALTER TABLE {raw:table_name}{raw:updates_text}',
2883
					array(
2884
						'table_name' => $table_info['Name'],
2885
						'updates_text' => substr($updates_text, 0, -1),
2886
					)
2887
				);
2888
			}
2889
2890
			// Now do the actual conversion (if still needed).
2891
			if ($charsets[$upcontext['charset_detected']] !== 'utf8')
2892
			{
2893
				if ($command_line)
2894
					echo 'Converting table ' . $table_info['Name'] . ' to UTF-8...';
2895
2896
				$smcFunc['db_query']('', '
2897
					ALTER TABLE {raw:table_name}
2898
					CONVERT TO CHARACTER SET utf8',
2899
					array(
2900
						'table_name' => $table_info['Name'],
2901
					)
2902
				);
2903
2904
				if ($command_line)
2905
					echo " done.\n";
2906
			}
2907
			// If this is XML to keep it nice for the user do one table at a time anyway!
2908
			if (isset($_GET['xml']) && $upcontext['cur_table_num'] < $upcontext['table_count'])
2909
				return upgradeExit();
2910
		}
2911
2912
		$prev_charset = empty($translation_tables[$upcontext['charset_detected']]) ? $charsets[$upcontext['charset_detected']] : $translation_tables[$upcontext['charset_detected']];
2913
2914
		$smcFunc['db_insert']('replace',
2915
			'{db_prefix}settings',
2916
			array('variable' => 'string', 'value' => 'string'),
2917
			array(array('global_character_set', 'UTF-8'), array('previousCharacterSet', $prev_charset)),
2918
			array('variable')
2919
		);
2920
2921
		// Store it in Settings.php too because it's needed before db connection.
2922
		// Hopefully this works...
2923
		require_once($sourcedir . '/Subs-Admin.php');
2924
		updateSettingsFile(array('db_character_set' => '\'utf8\''));
2925
2926
		// The conversion might have messed up some serialized strings. Fix them!
2927
		$request = $smcFunc['db_query']('', '
2928
			SELECT id_action, extra
2929
			FROM {db_prefix}log_actions
2930
			WHERE action IN ({string:remove}, {string:delete})',
2931
			array(
2932
				'remove' => 'remove',
2933
				'delete' => 'delete',
2934
			)
2935
		);
2936
		while ($row = $smcFunc['db_fetch_assoc']($request))
2937
		{
2938
			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)
2939
				$smcFunc['db_query']('', '
2940
					UPDATE {db_prefix}log_actions
2941
					SET extra = {string:extra}
2942
					WHERE id_action = {int:current_action}',
2943
					array(
2944
						'current_action' => $row['id_action'],
2945
						'extra' => $matches[1] . strlen($matches[3]) . ':"' . $matches[3] . '"' . $matches[4],
2946
					)
2947
				);
2948
		}
2949
		$smcFunc['db_free_result']($request);
2950
2951
		if ($upcontext['dropping_index'] && $command_line)
2952
		{
2953
			echo "\nYour fulltext search index was dropped to facilitate the conversion. You will need to recreate it.";
2954
			flush();
2955
		}
2956
	}
2957
	$_GET['substep'] = 0;
2958
	return false;
2959
}
2960
2961
function serialize_to_json()
2962
{
2963
	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...
2964
2965
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'serialize_json_xml' : 'serialize_json';
2966
	// First thing's first - did we already do this?
2967
	if (!empty($modSettings['json_done']))
2968
	{
2969
		if ($command_line)
2970
			return DeleteUpgrade();
2971
		else
2972
			return true;
2973
	}
2974
2975
	// Done it already - js wise?
2976
	if (!empty($_POST['json_done']))
2977
		return true;
2978
2979
	// List of tables affected by this function
2980
	// 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...
2981
	// If 3rd item in array is true, it indicates that col1 could be empty...
2982
	$tables = array(
2983
		'background_tasks' => array('id_task', 'task_data'),
2984
		'log_actions' => array('id_action', 'extra'),
2985
		'log_online' => array('session', 'url'),
2986
		'log_packages' => array('id_install', 'db_changes', 'failed_steps', 'credits'),
2987
		'log_spider_hits' => array('id_hit', 'url'),
2988
		'log_subscribed' => array('id_sublog', 'pending_details'),
2989
		'pm_rules' => array('id_rule', 'criteria', 'actions'),
2990
		'qanda' => array('id_question', 'answers'),
2991
		'subscriptions' => array('id_subscribe', 'cost'),
2992
		'user_alerts' => array('id_alert', 'extra', true),
2993
		'user_drafts' => array('id_draft', 'to_list', true),
2994
		// These last two are a bit different - we'll handle those separately
2995
		'settings' => array(),
2996
		'themes' => array()
2997
	);
2998
2999
	// Set up some context stuff...
3000
	// Because we're not using numeric indices, we need this to figure out the current table name...
3001
	$keys = array_keys($tables);
3002
3003
	$upcontext['page_title'] = 'Converting to JSON';
3004
	$upcontext['table_count'] = count($keys);
3005
	$upcontext['cur_table_num'] = $_GET['substep'];
3006
	$upcontext['cur_table_name'] = isset($keys[$_GET['substep']]) ? $keys[$_GET['substep']] : $keys[0];
3007
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3008
3009 View Code Duplication
	foreach ($keys as $id => $table)
3010
		if ($id < $_GET['substep'])
3011
			$upcontext['previous_tables'][] = $table;
3012
3013
	if ($command_line)
3014
		echo 'Converting data from serialize() to json_encode().';
3015
3016
	if (!$support_js || isset($_GET['xml']))
3017
	{
3018
		// Fix the data in each table
3019
		for ($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
3020
		{
3021
			$upcontext['cur_table_name'] = isset($keys[$substep + 1]) ? $keys[$substep + 1] : $keys[$substep];
3022
			$upcontext['cur_table_num'] = $substep + 1;
3023
3024
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3025
3026
			// Do we need to pause?
3027
			nextSubstep($substep);
3028
3029
			// Initialize a few things...
3030
			$where = '';
3031
			$vars = array();
3032
			$table = $keys[$substep];
3033
			$info = $tables[$table];
3034
3035
			// Now the fun - build our queries and all that fun stuff
3036
			if ($table == 'settings')
3037
			{
3038
				// Now a few settings...
3039
				$serialized_settings = array(
3040
					'attachment_basedirectories',
3041
					'attachmentUploadDir',
3042
					'cal_today_birthday',
3043
					'cal_today_event',
3044
					'cal_today_holiday',
3045
					'displayFields',
3046
					'last_attachments_directory',
3047
					'memberlist_cache',
3048
					'search_custom_index_config',
3049
					'spider_name_cache'
3050
				);
3051
3052
				// Loop through and fix these...
3053
				$new_settings = array();
3054
				if ($command_line)
3055
					echo "\n" . 'Fixing some settings...';
3056
3057
				foreach ($serialized_settings as $var)
3058
				{
3059
					if (isset($modSettings[$var]))
3060
					{
3061
						// Attempt to unserialize the setting
3062
						$temp = @safe_unserialize($modSettings[$var]);
3063
						if (!$temp && $command_line)
3064
							echo "\n - Failed to unserialize the '" . $var . "' setting. Skipping.";
3065
						elseif ($temp !== false)
3066
							$new_settings[$var] = json_encode($temp);
3067
					}
3068
				}
3069
3070
				// Update everything at once
3071
				if (!function_exists('cache_put_data'))
3072
					require_once($sourcedir . '/Load.php');
3073
				updateSettings($new_settings, true);
3074
3075
				if ($command_line)
3076
					echo ' done.';
3077
			}
3078
			elseif ($table == 'themes')
3079
			{
3080
				// Finally, fix the admin prefs. Unfortunately this is stored per theme, but hopefully they only have one theme installed at this point...
3081
				$query = $smcFunc['db_query']('', '
3082
					SELECT id_member, id_theme, value FROM {db_prefix}themes
3083
					WHERE variable = {string:admin_prefs}',
3084
						array(
3085
							'admin_prefs' => 'admin_preferences'
3086
						)
3087
				);
3088
3089
				if ($smcFunc['db_num_rows']($query) != 0)
3090
				{
3091
					while ($row = $smcFunc['db_fetch_assoc']($query))
3092
					{
3093
						$temp = @safe_unserialize($row['value']);
3094
3095
						if ($command_line)
3096
						{
3097
							if ($temp === false)
3098
								echo "\n" . 'Unserialize of admin_preferences for user ' . $row['id_member'] . ' failed. Skipping.';
3099
							else
3100
								echo "\n" . 'Fixing admin preferences...';
3101
						}
3102
3103
						if ($temp !== false)
3104
						{
3105
							$row['value'] = json_encode($temp);
3106
3107
							// Even though we have all values from the table, UPDATE is still faster than REPLACE
3108
							$smcFunc['db_query']('', '
3109
								UPDATE {db_prefix}themes
3110
								SET value = {string:prefs}
3111
								WHERE id_theme = {int:theme}
3112
									AND id_member = {int:member}
3113
									AND variable = {string:admin_prefs}',
3114
								array(
3115
									'prefs' => $row['value'],
3116
									'theme' => $row['id_theme'],
3117
									'member' => $row['id_member'],
3118
									'admin_prefs' => 'admin_preferences'
3119
								)
3120
							);
3121
3122
							if ($command_line)
3123
								echo ' done.';
3124
						}
3125
					}
3126
3127
					$smcFunc['db_free_result']($query);
3128
				}
3129
			}
3130
			else
3131
			{
3132
				// First item is always the key...
3133
				$key = $info[0];
3134
				unset($info[0]);
3135
3136
				// Now we know what columns we have and such...
3137
				if (count($info) == 2 && $info[2] === true)
3138
				{
3139
					$col_select = $info[1];
3140
					$where = ' WHERE ' . $info[1] . ' != {empty}';
3141
				}
3142
				else
3143
				{
3144
					$col_select = implode(', ', $info);
3145
				}
3146
3147
				$query = $smcFunc['db_query']('', '
3148
					SELECT ' . $key . ', ' . $col_select . '
3149
					FROM {db_prefix}' . $table . $where,
3150
					array()
3151
				);
3152
3153
				if ($smcFunc['db_num_rows']($query) != 0)
3154
				{
3155
					if ($command_line)
3156
					{
3157
						echo "\n" . ' +++ Fixing the "' . $table . '" table...';
3158
						flush();
3159
					}
3160
3161
					while ($row = $smcFunc['db_fetch_assoc']($query))
3162
					{
3163
						$update = '';
3164
3165
						// We already know what our key is...
3166
						foreach ($info as $col)
3167
						{
3168
							if ($col !== true && $row[$col] != '')
3169
							{
3170
								$temp = @safe_unserialize($row[$col]);
3171
3172
								if ($temp === false && $command_line)
3173
								{
3174
									echo "\nFailed to unserialize " . $row[$col] . "... Skipping\n";
3175
								}
3176
								else
3177
								{
3178
									$row[$col] = json_encode($temp);
3179
3180
									// Build our SET string and variables array
3181
									$update .= (empty($update) ? '' : ', ') . $col . ' = {string:' . $col . '}';
3182
									$vars[$col] = $row[$col];
3183
								}
3184
							}
3185
						}
3186
3187
						$vars[$key] = $row[$key];
3188
3189
						// In a few cases, we might have empty data, so don't try to update in those situations...
3190
						if (!empty($update))
3191
						{
3192
							$smcFunc['db_query']('', '
3193
								UPDATE {db_prefix}' . $table . '
3194
								SET ' . $update . '
3195
								WHERE ' . $key . ' = {' . ($key == 'session' ? 'string' : 'int') . ':' . $key . '}',
3196
								$vars
3197
							);
3198
						}
3199
					}
3200
3201
					if ($command_line)
3202
						echo ' done.';
3203
3204
					// Free up some memory...
3205
					$smcFunc['db_free_result']($query);
3206
				}
3207
			}
3208
			// If this is XML to keep it nice for the user do one table at a time anyway!
3209
			if (isset($_GET['xml']))
3210
				return upgradeExit();
3211
		}
3212
3213
		if ($command_line)
3214
		{
3215
			echo "\n" . 'Successful.' . "\n";
3216
			flush();
3217
		}
3218
		$upcontext['step_progress'] = 100;
3219
3220
		// Last but not least, insert a dummy setting so we don't have to do this again in the future...
3221
		updateSettings(array('json_done' => true));
3222
3223
		$_GET['substep'] = 0;
3224
		// Make sure we move on!
3225
		if ($command_line)
3226
			return DeleteUpgrade();
3227
3228
		return true;
3229
	}
3230
3231
	// If this fails we just move on to deleting the upgrade anyway...
3232
	$_GET['substep'] = 0;
3233
	return false;
3234
}
3235
3236
/******************************************************************************
3237
******************* Templates are below this point ****************************
3238
******************************************************************************/
3239
3240
// This is what is displayed if there's any chmod to be done. If not it returns nothing...
3241
function template_chmod()
3242
{
3243
	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...
3244
3245
	// Don't call me twice!
3246
	if (!empty($upcontext['chmod_called']))
3247
		return;
3248
3249
	$upcontext['chmod_called'] = true;
3250
3251
	// Nothing?
3252
	if (empty($upcontext['chmod']['files']) && empty($upcontext['chmod']['ftp_error']))
3253
		return;
3254
3255
	// Was it a problem with Windows?
3256
	if (!empty($upcontext['chmod']['ftp_error']) && $upcontext['chmod']['ftp_error'] == 'total_mess')
3257
	{
3258
		echo '
3259
			<div class="error_message red">
3260
				The following files need to be writable to continue the upgrade. Please ensure the Windows permissions are correctly set to allow this:<br>
3261
				<ul style="margin: 2.5ex; font-family: monospace;">
3262
					<li>' . implode('</li>
3263
					<li>', $upcontext['chmod']['files']) . '</li>
3264
				</ul>
3265
			</div>';
3266
3267
		return false;
3268
	}
3269
3270
	echo '
3271
		<div class="panel">
3272
			<h2>', $txt['upgrade_ftp_login'], '</h2>
3273
			<h3>', $txt['upgrade_ftp_perms'], '</h3>
3274
			<script>
3275
				function warning_popup()
3276
				{
3277
					popup = window.open(\'\',\'popup\',\'height=150,width=400,scrollbars=yes\');
3278
					var content = popup.document;
3279
					content.write(\'<!DOCTYPE html>\n\');
3280
					content.write(\'<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>\n\t<head>\n\t\t<meta name="robots" content="noindex">\n\t\t\');
3281
					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\');
3282
					content.write(\'<div class="windowbg description">\n\t\t\t<h4>', $txt['upgrade_ftp_files'], '</h4>\n\t\t\t\');
3283
					content.write(\'<p>', implode('<br>\n\t\t\t', $upcontext['chmod']['files']), '</p>\n\t\t\t\');';
3284
3285
	if (isset($upcontext['systemos']) && $upcontext['systemos'] == 'linux')
3286
		echo '
3287
					content.write(\'<hr>\n\t\t\t\');
3288
					content.write(\'<p>', $txt['upgrade_ftp_shell'], '</p>\n\t\t\t\');
3289
					content.write(\'<tt># chmod a+w ', implode(' ', $upcontext['chmod']['files']), '</tt>\n\t\t\t\');';
3290
3291
	echo '
3292
					content.write(\'<a href="javascript:self.close();">close</a>\n\t\t</div>\n\t</body>\n</html>\');
3293
					content.close();
3294
				}
3295
			</script>';
3296
3297
	if (!empty($upcontext['chmod']['ftp_error']))
3298
		echo '
3299
			<div class="error_message red">
3300
				', $txt['upgrade_ftp_error'], '<br><br>
3301
				<code>', $upcontext['chmod']['ftp_error'], '</code>
3302
			</div>
3303
			<br>';
3304
3305
	if (empty($upcontext['chmod_in_form']))
3306
		echo '
3307
	<form action="', $upcontext['form_url'], '" method="post">';
3308
3309
	echo '
3310
		<table width="520" border="0" align="center" style="margin-bottom: 1ex;">
3311
			<tr>
3312
				<td width="26%" valign="top" class="textbox"><label for="ftp_server">', $txt['ftp_server'], ':</label></td>
3313
				<td>
3314
					<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>
3315
					<input type="text" size="30" name="ftp_server" id="ftp_server" value="', isset($upcontext['chmod']['server']) ? $upcontext['chmod']['server'] : 'localhost', '" style="width: 70%;">
3316
					<div class="smalltext block">', $txt['ftp_server_info'], '</div>
3317
				</td>
3318
			</tr><tr>
3319
				<td width="26%" valign="top" class="textbox"><label for="ftp_username">', $txt['ftp_username'], ':</label></td>
3320
				<td>
3321
					<input type="text" size="50" name="ftp_username" id="ftp_username" value="', isset($upcontext['chmod']['username']) ? $upcontext['chmod']['username'] : '', '" style="width: 99%;">
3322
					<div class="smalltext block">', $txt['ftp_username_info'], '</div>
3323
				</td>
3324
			</tr><tr>
3325
				<td width="26%" valign="top" class="textbox"><label for="ftp_password">', $txt['ftp_password'], ':</label></td>
3326
				<td>
3327
					<input type="password" size="50" name="ftp_password" id="ftp_password" style="width: 99%;">
3328
					<div class="smalltext block">', $txt['ftp_password_info'], '</div>
3329
				</td>
3330
			</tr><tr>
3331
				<td width="26%" valign="top" class="textbox"><label for="ftp_path">', $txt['ftp_path'], ':</label></td>
3332
				<td style="padding-bottom: 1ex;">
3333
					<input type="text" size="50" name="ftp_path" id="ftp_path" value="', isset($upcontext['chmod']['path']) ? $upcontext['chmod']['path'] : '', '" style="width: 99%;">
3334
					<div class="smalltext block">', !empty($upcontext['chmod']['path']) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'], '</div>
3335
				</td>
3336
			</tr>
3337
		</table>
3338
3339
		<div class="righttext" style="margin: 1ex;"><input type="submit" value="', $txt['ftp_connect'], '" class="button"></div>
3340
	</div>';
3341
3342
	if (empty($upcontext['chmod_in_form']))
3343
		echo '
3344
	</form>';
3345
}
3346
3347
function template_upgrade_above()
3348
{
3349
	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...
3350
3351
	echo '<!DOCTYPE html>
3352
<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>
3353
	<head>
3354
		<meta charset="', isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8', '">
3355
		<meta name="robots" content="noindex">
3356
		<title>', $txt['upgrade_upgrade_utility'], '</title>
3357
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/index.css?alp21">
3358
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/install.css?alp21">
3359
		', $txt['lang_rtl'] == true ? '<link rel="stylesheet" href="' . $settings['default_theme_url'] . '/css/rtl.css?alp21">' : '', '
3360
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
3361
		<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
3362
		<script>
3363
			var smf_scripturl = \'', $upgradeurl, '\';
3364
			var smf_charset = \'', (empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'UTF-8' : $txt['lang_character_set']) : $modSettings['global_character_set']), '\';
3365
			var startPercent = ', $upcontext['overall_percent'], ';
3366
3367
			// This function dynamically updates the step progress bar - and overall one as required.
3368
			function updateStepProgress(current, max, overall_weight)
3369
			{
3370
				// What out the actual percent.
3371
				var width = parseInt((current / max) * 100);
3372
				if (document.getElementById(\'step_progress\'))
3373
				{
3374
					document.getElementById(\'step_progress\').style.width = width + "%";
3375
					setInnerHTML(document.getElementById(\'step_text\'), width + "%");
3376
				}
3377
				if (overall_weight && document.getElementById(\'overall_progress\'))
3378
				{
3379
					overall_width = parseInt(startPercent + width * (overall_weight / 100));
3380
					document.getElementById(\'overall_progress\').style.width = overall_width + "%";
3381
					setInnerHTML(document.getElementById(\'overall_text\'), overall_width + "%");
3382
				}
3383
			}
3384
		</script>
3385
	</head>
3386
	<body>
3387
	<div id="footerfix">
3388
		<div id="header">
3389
			<h1 class="forumtitle">', $txt['upgrade_upgrade_utility'], '</h1>
3390
			<img id="smflogo" src="', $settings['default_theme_url'], '/images/smflogo.svg" alt="Simple Machines Forum" title="Simple Machines Forum">
3391
		</div>
3392
	<div id="wrapper">
3393
		<div id="upper_section">
3394
			<div id="inner_section">
3395
				<div id="inner_wrap">
3396
				</div>
3397
			</div>
3398
		</div>
3399
		<div id="content_section">
3400
		<div id="main_content_section">
3401
			<div id="main_steps">
3402
				<h2>', $txt['upgrade_progress'], '</h2>
3403
				<ul>';
3404
3405 View Code Duplication
	foreach ($upcontext['steps'] as $num => $step)
3406
		echo '
3407
						<li class="', $num < $upcontext['current_step'] ? 'stepdone' : ($num == $upcontext['current_step'] ? 'stepcurrent' : 'stepwaiting'), '">', $txt['upgrade_step'], ' ', $step[0], ': ', $step[1], '</li>';
3408
3409
	echo '
3410
					</ul>
3411
			</div>
3412
3413
			<div id="progress_bar">
3414
				<div id="overall_text">', $upcontext['overall_percent'], '%</div>
3415
				<div id="overall_progress" style="width: ', $upcontext['overall_percent'], '%;">
3416
					<span>', $txt['upgrade_overall_progress'], '</span>
3417
				</div>
3418
			</div>';
3419
3420
	if (isset($upcontext['step_progress']))
3421
		echo '
3422
				<br>
3423
				<br>
3424
				<div id="progress_bar_step">
3425
					<div id="step_text">', $upcontext['step_progress'], '%</div>
3426
					<div id="step_progress" style="width: ', $upcontext['step_progress'], '%;background-color: #ffd000;">
3427
						<span>', $txt['upgrade_step_progress'], '</span>
3428
					</div>
3429
				</div>';
3430
3431
	echo '
3432
				<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>
3433
				<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', ';">
3434
					<div id="substep_text" style="color: #000; position: absolute; margin-left: -5em;">', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : '', '%</div>
3435
					<div id="substep_progress" style="width: ', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%; height: 12pt; z-index: 1; background-color: #eebaf4;">&nbsp;</div>
3436
				</div>';
3437
3438
	// How long have we been running this?
3439
	$elapsed = time() - $upcontext['started'];
3440
	$mins = (int) ($elapsed / 60);
3441
	$seconds = $elapsed - $mins * 60;
3442
	echo '
3443
								<br> <br> <br> <br> <br>
3444
								<div class="smalltext" style="padding: 5px; text-align: center;"><br>', $txt['upgrade_time_elapsed'], ':
3445
									<span id="mins_elapsed">', $mins, '</span> ', $txt['upgrade_time_mins'], ', <span id="secs_elapsed">', $seconds, '</span> ', $txt['upgrade_time_secs'], '.
3446
								</div>';
3447
	echo '
3448
			</div>
3449
			</div>
3450
			<div id="main_screen" class="clear">
3451
				<h2>', $upcontext['page_title'], '</h2>
3452
				<div class="panel">
3453
					<div style="max-height: 360px; overflow: auto;">';
3454
}
3455
3456
function template_upgrade_below()
3457
{
3458
	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...
3459
3460
	if (!empty($upcontext['pause']))
3461
		echo '
3462
								<em>', $txt['upgrade_incomplete'], '.</em><br>
3463
3464
								<h2 style="margin-top: 2ex;">', $txt['upgrade_not_quite_done'], '</h2>
3465
								<h3>
3466
									', $txt['upgrade_paused_overload'], '
3467
								</h3>';
3468
3469
	if (!empty($upcontext['custom_warning']))
3470
		echo '
3471
								<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3472
									<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3473
									<strong style="text-decoration: underline;">', $txt['upgrade_note'], '</strong><br>
3474
									<div style="padding-left: 6ex;">', $upcontext['custom_warning'], '</div>
3475
								</div>';
3476
3477
	echo '
3478
								<div class="righttext" style="margin: 1ex;">';
3479
3480
	if (!empty($upcontext['continue']))
3481
		echo '
3482
									<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '"', $upcontext['continue'] == 2 ? ' disabled' : '', ' class="button">';
3483
	if (!empty($upcontext['skip']))
3484
		echo '
3485
									<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="dontSubmit = true; document.getElementById(\'contbutt\').disabled = \'disabled\'; return true;" class="button">';
3486
3487
	echo '
3488
								</div>
3489
							</form>
3490
						</div>
3491
				</div>
3492
			</div>
3493
			</div>
3494
		</div>
3495
		<div id="footer">
3496
			<ul>
3497
				<li class="copyright"><a href="https://www.simplemachines.org/" title="Simple Machines Forum" target="_blank" rel="noopener">SMF &copy; 2017, Simple Machines</a></li>
3498
			</ul>
3499
		</div>
3500
	</body>
3501
</html>';
3502
3503
	// Are we on a pause?
3504
	if (!empty($upcontext['pause']))
3505
	{
3506
		echo '
3507
		<script>
3508
			window.onload = doAutoSubmit;
3509
			var countdown = 3;
3510
			var dontSubmit = false;
3511
3512
			function doAutoSubmit()
3513
			{
3514
				if (countdown == 0 && !dontSubmit)
3515
					document.upform.submit();
3516
				else if (countdown == -1)
3517
					return;
3518
3519
				document.getElementById(\'contbutt\').value = "', $txt['upgrade_continue'], ' (" + countdown + ")";
3520
				countdown--;
3521
3522
				setTimeout("doAutoSubmit();", 1000);
3523
			}
3524
		</script>';
3525
	}
3526
}
3527
3528
function template_xml_above()
3529
{
3530
	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...
3531
3532
	echo '<', '?xml version="1.0" encoding="UTF-8"?', '>
3533
	<smf>';
3534
3535
	if (!empty($upcontext['get_data']))
3536
		foreach ($upcontext['get_data'] as $k => $v)
3537
			echo '
3538
		<get key="', $k, '">', $v, '</get>';
3539
}
3540
3541
function template_xml_below()
3542
{
3543
	echo '
3544
		</smf>';
3545
}
3546
3547
function template_error_message()
3548
{
3549
	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...
3550
3551
	echo '
3552
	<div class="error_message red">
3553
		', $upcontext['error_msg'], '
3554
		<br>
3555
		<a href="', $_SERVER['PHP_SELF'], '">Click here to try again.</a>
0 ignored issues
show
Security Cross-Site Scripting introduced by
$_SERVER['PHP_SELF'] can contain request data and is used in output context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Fetching key PHP_SELF from $_SERVER
    in other/upgrade.php on line 3555

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
3556
	</div>';
3557
}
3558
3559
function template_welcome_message()
0 ignored issues
show
Best Practice introduced by
The function template_welcome_message() has been defined more than once; this definition is ignored, only the first definition in other/install.php (L1993-2042) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
3560
{
3561
	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...
3562
3563
	echo '
3564
		<script src="https://www.simplemachines.org/smf/current-version.js?version=' . SMF_VERSION . '"></script>
3565
			<h3>', sprintf($txt['upgrade_ready_proceed'], SMF_VERSION), '</h3>
3566
	<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">
3567
		<input type="hidden" name="', $upcontext['login_token_var'], '" value="', $upcontext['login_token'], '">
3568
		<div id="version_warning" style="margin: 2ex; padding: 2ex; border: 2px dashed #a92174; color: black; background-color: #fbbbe2; display: none;">
3569
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3570
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3571
			<div style="padding-left: 6ex;">
3572
				', sprintf($txt['upgrade_warning_out_of_date'], SMF_VERSION), '
3573
			</div>
3574
		</div>';
3575
3576
	$upcontext['chmod_in_form'] = true;
3577
	template_chmod();
3578
3579
	// For large, pre 1.1 RC2 forums give them a warning about the possible impact of this upgrade!
3580
	if ($upcontext['is_large_forum'])
3581
		echo '
3582
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3583
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3584
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3585
			<div style="padding-left: 6ex;">
3586
				', $txt['upgrade_warning_lots_data'], '
3587
			</div>
3588
		</div>';
3589
3590
	// A warning message?
3591
	if (!empty($upcontext['warning']))
3592
		echo '
3593
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3594
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3595
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3596
			<div style="padding-left: 6ex;">
3597
				', $upcontext['warning'], '
3598
			</div>
3599
		</div>';
3600
3601
	// Paths are incorrect?
3602
	echo '
3603
		<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">
3604
			<div style="float: left; width: 2ex; font-size: 2em; color: black;">!!</div>
3605
			<strong style="text-decoration: underline;">', $txt['upgrade_critical_error'], '</strong><br>
3606
			<div style="padding-left: 6ex;">
3607
				', $txt['upgrade_error_script_js'], '
3608
			</div>
3609
		</div>';
3610
3611
	// Is there someone already doing this?
3612
	if (!empty($upcontext['user']['id']) && (time() - $upcontext['started'] < 72600 || time() - $upcontext['updated'] < 3600))
3613
	{
3614
		$ago = time() - $upcontext['started'];
3615
		if ($ago < 60)
3616
			$ago = $ago . ' seconds';
3617
		elseif ($ago < 3600)
3618
			$ago = (int) ($ago / 60) . ' minutes';
3619
		else
3620
			$ago = (int) ($ago / 3600) . ' hours';
3621
3622
		$active = time() - $upcontext['updated'];
3623
		if ($active < 60)
3624
			$updated = $active . ' seconds';
3625
		elseif ($active < 3600)
3626
			$updated = (int) ($active / 60) . ' minutes';
3627
		else
3628
			$updated = (int) ($active / 3600) . ' hours';
3629
3630
		echo '
3631
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3632
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3633
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3634
			<div style="padding-left: 6ex;">
3635
				&quot;', $upcontext['user']['name'], '&quot; has been running the upgrade script for the last ', $ago, ' - and was last active ', $updated, ' ago.';
3636
3637
		if ($active < 600)
3638
			echo '
3639
				', $txt['upgrade_run_script'], ' ', $upcontext['user']['name'],' ', $txt['upgrade_run_script2'], '';
3640
3641
		if ($active > $upcontext['inactive_timeout'])
3642
			echo '
3643
				<br><br>',$txt['upgrade_run'], '';
3644
		else
3645
			echo '
3646
				<br><br>', $txt['upgrade_script_timeout'], ' ', $upcontext['user']['name'], ' ', $txt['upgrade_script_timeout2'], ' ', ($upcontext['inactive_timeout'] > 120 ? round($upcontext['inactive_timeout'] / 60, 1) . ' minutes!' : $upcontext['inactive_timeout'] . ' seconds!');
3647
3648
		echo '
3649
			</div>
3650
		</div>';
3651
	}
3652
3653
	echo '
3654
			<strong>', $txt['upgrade_admin_login'], ' ', $disable_security ? '(DISABLED)' : '', '</strong>
3655
			<h3>', $txt['upgrade_sec_login'], '</h3>
3656
			<table>
3657
				<tr valign="top">
3658
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>', $txt['upgrade_username'], '</strong></td>
3659
					<td>
3660
						<input type="text" name="user" value="', !empty($upcontext['username']) ? $upcontext['username'] : '', '"', $disable_security ? ' disabled' : '', '>';
3661
3662
	if (!empty($upcontext['username_incorrect']))
3663
		echo '
3664
						<div class="smalltext" style="color: red;">', $txt['upgrade_wrong_username'], '</div>';
3665
3666
	echo '
3667
					</td>
3668
				</tr>
3669
				<tr valign="top">
3670
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>', $txt['upgrade_password'], '</strong></td>
3671
					<td>
3672
						<input type="password" name="passwrd" value=""', $disable_security ? ' disabled' : '', '>
3673
						<input type="hidden" name="hash_passwrd" value="">';
3674
3675
	if (!empty($upcontext['password_failed']))
3676
		echo '
3677
						<div class="smalltext" style="color: red;">', $txt['upgrade_wrong_password'], '</div>';
3678
3679
	echo '
3680
					</td>
3681
				</tr>';
3682
3683
	// Can they continue?
3684
	if (!empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] >= $upcontext['inactive_timeout'] && $upcontext['user']['step'] > 1)
3685
	{
3686
		echo '
3687
				<tr>
3688
					<td colspan="2">
3689
						<label for="cont"><input type="checkbox" id="cont" name="cont" checked>', $txt['upgrade_continue_step'], '</label>
3690
					</td>
3691
				</tr>';
3692
	}
3693
3694
	echo '
3695
			</table><br>
3696
			<span class="smalltext">
3697
				', $txt['upgrade_bypass'], '
3698
			</span>
3699
			<input type="hidden" name="login_attempt" id="login_attempt" value="1">
3700
			<input type="hidden" name="js_works" id="js_works" value="0">';
3701
3702
	// Say we want the continue button!
3703
	$upcontext['continue'] = !empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] < $upcontext['inactive_timeout'] ? 2 : 1;
3704
3705
	// This defines whether javascript is going to work elsewhere :D
3706
	echo '
3707
		<script>
3708
			if (\'XMLHttpRequest\' in window && document.getElementById(\'js_works\'))
3709
				document.getElementById(\'js_works\').value = 1;
3710
3711
			// Latest version?
3712
			function smfCurrentVersion()
3713
			{
3714
				var smfVer, yourVer;
3715
3716
				if (!(\'smfVersion\' in window))
3717
					return;
3718
3719
				window.smfVersion = window.smfVersion.replace(/SMF\s?/g, \'\');
3720
3721
				smfVer = document.getElementById(\'smfVersion\');
3722
				yourVer = document.getElementById(\'yourVersion\');
3723
3724
				setInnerHTML(smfVer, window.smfVersion);
3725
3726
				var currentVersion = getInnerHTML(yourVer);
3727
				if (currentVersion < window.smfVersion)
3728
					document.getElementById(\'version_warning\').style.display = \'\';
3729
			}
3730
			addLoadEvent(smfCurrentVersion);
3731
3732
			// This checks that the script file even exists!
3733
			if (typeof(smfSelectText) == \'undefined\')
3734
				document.getElementById(\'js_script_missing_error\').style.display = \'\';
3735
3736
		</script>';
3737
}
3738
3739
function template_upgrade_options()
3740
{
3741
	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...
3742
3743
	echo '
3744
			<h3>', $txt['upgrade_areyouready'], '</h3>
3745
			<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">';
3746
3747
	// Warning message?
3748
	if (!empty($upcontext['upgrade_options_warning']))
3749
		echo '
3750
		<div style="margin: 1ex; padding: 1ex; border: 1px dashed #cc3344; color: black; background-color: #ffe4e9;">
3751
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3752
			<strong style="text-decoration: underline;">Warning!</strong><br>
3753
			<div style="padding-left: 4ex;">
3754
				', $upcontext['upgrade_options_warning'], '
3755
			</div>
3756
		</div>';
3757
3758
	echo '
3759
				<table>
3760
					<tr valign="top">
3761
						<td width="2%">
3762
							<input type="checkbox" name="backup" id="backup" value="1">
3763
						</td>
3764
						<td width="100%">
3765
							<label for="backup">', $txt['upgrade_backup_table'], ' &quot;backup_' . $db_prefix . '&quot;.</label> (', $txt['upgrade_recommended'], ')
3766
						</td>
3767
					</tr>
3768
					<tr valign="top">
3769
						<td width="2%">
3770
							<input type="checkbox" name="maint" id="maint" value="1" checked>
3771
						</td>
3772
						<td width="100%">
3773
							<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>
3774
							<div id="mainmess" style="display: none;">
3775
								<strong class="smalltext">', $txt['upgrade_maintenance_title'], ' </strong><br>
3776
								<input type="text" name="maintitle" size="30" value="', htmlspecialchars($mtitle), '"><br>
3777
								<strong class="smalltext">', $txt['upgrade_maintenace_message'], ' </strong><br>
3778
								<textarea name="mainmessage" rows="3" cols="50">', htmlspecialchars($mmessage), '</textarea>
3779
							</div>
3780
						</td>
3781
					</tr>
3782
					<tr valign="top">
3783
						<td width="2%">
3784
							<input type="checkbox" name="debug" id="debug" value="1">
3785
						</td>
3786
						<td width="100%">
3787
							<label for="debug">'.$txt['upgrade_debug_info'], '</label>
3788
						</td>
3789
					</tr>
3790
					<tr valign="top">
3791
						<td width="2%">
3792
							<input type="checkbox" name="empty_error" id="empty_error" value="1">
3793
						</td>
3794
						<td width="100%">
3795
							<label for="empty_error">', $txt['upgrade_empty_errlog'], '</label>
3796
						</td>
3797
					</tr>';
3798
3799
	if (!empty($upcontext['karma_installed']['good']) || !empty($upcontext['karma_installed']['bad']))
3800
		echo '
3801
					<tr valign="top">
3802
						<td width="2%">
3803
							<input type="checkbox" name="delete_karma" id="delete_karma" value="1">
3804
						</td>
3805
						<td width="100%">
3806
							<label for="delete_karma">', $txt['upgrade_delete_karma'], '</label>
3807
						</td>
3808
					</tr>';
3809
3810
	echo '
3811
					<tr valign="top">
3812
						<td width="2%">
3813
							<input type="checkbox" name="stats" id="stats" value="1"', empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']) ? '' : ' checked="checked"', ' />
3814
						</td>
3815
						<td width="100%">
3816
							<label for="stat">
3817
								', $txt['upgrade_stats_collection'], '<br>
3818
								<span class="smalltext">', $txt['upgrade_stats_info'], '</a></span>
3819
							</label>
3820
						</td>
3821
					</tr>
3822
				</table>
3823
				<input type="hidden" name="upcont" value="1">';
3824
3825
	// We need a normal continue button here!
3826
	$upcontext['continue'] = 1;
3827
}
3828
3829
// Template for the database backup tool/
3830
function template_backup_database()
3831
{
3832
	global $upcontext, $support_js, $is_debug, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

    public function myFunction() {
        // Do something
    }
}
Loading history...
3833
3834
	echo '
3835
			<h3>', $txt['upgrade_wait'], '</h3>';
3836
3837
	echo '
3838
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
3839
			<input type="hidden" name="backup_done" id="backup_done" value="0">
3840
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
3841
			<div id="debug_section" style="height: ', ($is_debug ? '115' : '12') , 'px; overflow: auto;">
3842
			<span id="debuginfo"></span>
3843
			</div>';
3844
3845
	// Dont any tables so far?
3846 View Code Duplication
	if (!empty($upcontext['previous_tables']))
3847
		foreach ($upcontext['previous_tables'] as $table)
3848
			echo '
3849
			<br>', $txt['upgrade_completed_table'], ' &quot;', $table, '&quot;.';
3850
3851
	echo '
3852
			<h3 id="current_tab_div">', $txt['upgrade_current_table'], ' &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
3853
			<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>';
3854
3855
	// Continue please!
3856
	$upcontext['continue'] = $support_js ? 2 : 1;
3857
3858
	// If javascript allows we want to do this using XML.
3859 View Code Duplication
	if ($support_js)
3860
	{
3861
		echo '
3862
		<script>
3863
			var lastTable = ', $upcontext['cur_table_num'], ';
3864
			function getNextTables()
3865
			{
3866
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
3867
			}
3868
3869
			// Got an update!
3870
			function onBackupUpdate(oXMLDoc)
3871
			{
3872
				var sCurrentTableName = "";
3873
				var iTableNum = 0;
3874
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
3875
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
3876
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
3877
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
3878
3879
				// Update the page.
3880
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
3881
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
3882
				lastTable = iTableNum;
3883
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
3884
3885
		// If debug flood the screen.
3886
		if ($is_debug)
3887
			echo '
3888
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
3889
3890
				if (document.getElementById(\'debug_section\').scrollHeight)
3891
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
3892
3893
		echo '
3894
				// Get the next update...
3895
				if (iTableNum == ', $upcontext['table_count'], ')
3896
				{
3897
					document.getElementById(\'commess\').style.display = "";
3898
					document.getElementById(\'current_tab_div\').style.display = "none";
3899
					document.getElementById(\'contbutt\').disabled = 0;
3900
					document.getElementById(\'backup_done\').value = 1;
3901
				}
3902
				else
3903
					getNextTables();
3904
			}
3905
			getNextTables();
3906
		//# sourceURL=dynamicScript-bkup.js
3907
		</script>';
3908
	}
3909
}
3910
3911
function template_backup_xml()
3912
{
3913
	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...
3914
3915
	echo '
3916
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
3917
}
3918
3919
// Here is the actual "make the changes" template!
3920
function template_database_changes()
3921
{
3922
	global $upcontext, $support_js, $is_debug, $timeLimitThreshold, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

    public function myFunction() {
        // Do something
    }
}
Loading history...
3923
3924
	if (empty($is_debug) && !empty($upcontext['upgrade_status']['debug']))
3925
		$is_debug = true;
3926
3927
	echo '
3928
		<h3>', $txt['upgrade_db_changes'], '</h3>
3929
		<h4 style="font-style: italic;">', $txt['upgrade_db_patient'], '</h4>';
3930
3931
	echo '
3932
		<form action="', $upcontext['form_url'], '&amp;filecount=', $upcontext['file_count'], '" name="upform" id="upform" method="post">
3933
		<input type="hidden" name="database_done" id="database_done" value="0">';
3934
3935
	// No javascript looks rubbish!
3936
	if (!$support_js)
3937
	{
3938
		foreach ($upcontext['actioned_items'] as $num => $item)
3939
		{
3940
			if ($num != 0)
3941
				echo ' Successful!';
3942
			echo '<br>' . $item;
3943
		}
3944 View Code Duplication
		if (!empty($upcontext['changes_complete']))
3945
		{
3946
			if ($is_debug)
3947
			{
3948
				$active = time() - $upcontext['started'];
3949
				$hours = floor($active / 3600);
3950
				$minutes = intval(($active / 60) % 60);
3951
				$seconds = intval($active % 60);
3952
3953
				$totalTime = '';
3954
				if ($hours > 0)
3955
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
3956
				if ($minutes > 0)
3957
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
3958
				if ($seconds > 0)
3959
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
3960
			}
3961
3962
			if ($is_debug && !empty($totalTime))
3963
				echo ' Successful! Completed in ', $totalTime, '<br><br>';
3964
			else
3965
				echo ' Successful!<br><br>';
3966
3967
			echo '<span id="commess" style="font-weight: bold;">', $txt['upgrade_db_complete'], '</span><br>';
3968
		}
3969
	}
3970
	else
3971
	{
3972
		// Tell them how many files we have in total.
3973
		if ($upcontext['file_count'] > 1)
3974
			echo '
3975
		<strong id="info1">', $txt['upgrade_script'], ' <span id="file_done">', $upcontext['cur_file_num'], '</span> of ', $upcontext['file_count'], '.</strong>';
3976
3977
		echo '
3978
		<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>
3979
		<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>';
3980
3981 View Code Duplication
		if ($is_debug)
3982
		{
3983
			if ($upcontext['current_debug_item_num'] == $upcontext['debug_items'])
3984
			{
3985
				$active = time() - $upcontext['started'];
3986
				$hours = floor($active / 3600);
3987
				$minutes = intval(($active / 60) % 60);
3988
				$seconds = intval($active % 60);
3989
3990
				$totalTime = '';
3991
				if ($hours > 0)
3992
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
3993
				if ($minutes > 0)
3994
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
3995
				if ($seconds > 0)
3996
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
3997
			}
3998
3999
			echo '
4000
			<br><span id="upgradeCompleted">';
4001
4002
			if (!empty($totalTime))
4003
				echo 'Completed in ', $totalTime, '<br>';
4004
4005
			echo '</span>
4006
			<div id="debug_section" style="height: 59px; overflow: auto;">
4007
			<span id="debuginfo"></span>
4008
			</div>';
4009
		}
4010
	}
4011
4012
	// Place for the XML error message.
4013
	echo '
4014
		<div id="error_block" style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9; display: ', empty($upcontext['error_message']) ? 'none' : '', ';">
4015
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4016
			<strong style="text-decoration: underline;">', $txt['upgrade_error'], '</strong><br>
4017
			<div style="padding-left: 6ex;" id="error_message">', isset($upcontext['error_message']) ? $upcontext['error_message'] : $txt['upgrade_unknown_error'], '</div>
4018
		</div>';
4019
4020
	// We want to continue at some point!
4021
	$upcontext['continue'] = $support_js ? 2 : 1;
4022
4023
	// If javascript allows we want to do this using XML.
4024
	if ($support_js)
4025
	{
4026
		echo '
4027
		<script>
4028
			var lastItem = ', $upcontext['current_debug_item_num'], ';
4029
			var sLastString = "', strtr($upcontext['current_debug_item_name'], array('"' => '&quot;')), '";
4030
			var iLastSubStepProgress = -1;
4031
			var curFile = ', $upcontext['cur_file_num'], ';
4032
			var totalItems = 0;
4033
			var prevFile = 0;
4034
			var retryCount = 0;
4035
			var testvar = 0;
4036
			var timeOutID = 0;
4037
			var getData = "";
4038
			var debugItems = ', $upcontext['debug_items'], ';';
4039
4040
		if ($is_debug)
4041
			echo '
4042
			var upgradeStartTime = ' . $upcontext['started'] . ';';
4043
4044
		echo '
4045
			function getNextItem()
4046
			{
4047
				// We want to track this...
4048
				if (timeOutID)
4049
					clearTimeout(timeOutID);
4050
				timeOutID = window.setTimeout("retTimeout()", ', (10 * $timeLimitThreshold), '000);
4051
4052
				getXMLDocument(\'', $upcontext['form_url'], '&xml&filecount=', $upcontext['file_count'], '&substep=\' + lastItem + getData, onItemUpdate);
4053
			}
4054
4055
			// Got an update!
4056
			function onItemUpdate(oXMLDoc)
4057
			{
4058
				var sItemName = "";
4059
				var sDebugName = "";
4060
				var iItemNum = 0;
4061
				var iSubStepProgress = -1;
4062
				var iDebugNum = 0;
4063
				var bIsComplete = 0;
4064
				getData = "";
4065
4066
				// We\'ve got something - so reset the timeout!
4067
				if (timeOutID)
4068
					clearTimeout(timeOutID);
4069
4070
				// Assume no error at this time...
4071
				document.getElementById("error_block").style.display = "none";
4072
4073
				// Are we getting some duff info?
4074
				if (!oXMLDoc.getElementsByTagName("item")[0])
4075
				{
4076
					// Too many errors?
4077
					if (retryCount > 15)
4078
					{
4079
						document.getElementById("error_block").style.display = "";
4080
						setInnerHTML(document.getElementById("error_message"), "Error retrieving information on step: " + (sDebugName == "" ? sLastString : sDebugName));';
4081
4082
	if ($is_debug)
4083
		echo '
4084
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4085
4086
	echo '
4087
					}
4088
					else
4089
					{
4090
						retryCount++;
4091
						getNextItem();
4092
					}
4093
					return false;
4094
				}
4095
4096
				// Never allow loops.
4097
				if (curFile == prevFile)
4098
				{
4099
					retryCount++;
4100
					if (retryCount > 10)
4101
					{
4102
						document.getElementById("error_block").style.display = "";
4103
						setInnerHTML(document.getElementById("error_message"), "Upgrade script appears to be going into a loop - step: " + sDebugName);';
4104
4105
	if ($is_debug)
4106
		echo '
4107
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4108
4109
	echo '
4110
					}
4111
				}
4112
				retryCount = 0;
4113
4114
				for (var i = 0; i < oXMLDoc.getElementsByTagName("item")[0].childNodes.length; i++)
4115
					sItemName += oXMLDoc.getElementsByTagName("item")[0].childNodes[i].nodeValue;
4116
				for (var i = 0; i < oXMLDoc.getElementsByTagName("debug")[0].childNodes.length; i++)
4117
					sDebugName += oXMLDoc.getElementsByTagName("debug")[0].childNodes[i].nodeValue;
4118
				for (var i = 0; i < oXMLDoc.getElementsByTagName("get").length; i++)
4119
				{
4120
					getData += "&" + oXMLDoc.getElementsByTagName("get")[i].getAttribute("key") + "=";
4121
					for (var j = 0; j < oXMLDoc.getElementsByTagName("get")[i].childNodes.length; j++)
4122
					{
4123
						getData += oXMLDoc.getElementsByTagName("get")[i].childNodes[j].nodeValue;
4124
					}
4125
				}
4126
4127
				iItemNum = oXMLDoc.getElementsByTagName("item")[0].getAttribute("num");
4128
				iDebugNum = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("num"));
4129
				bIsComplete = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("complete"));
4130
				iSubStepProgress = parseFloat(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("percent"));
4131
				sLastString = sDebugName + " (Item: " + iDebugNum + ")";
4132
4133
				curFile = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("num"));
4134
				debugItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("debug_items"));
4135
				totalItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("items"));
4136
4137
				// If we have an error we haven\'t completed!
4138
				if (oXMLDoc.getElementsByTagName("error")[0] && bIsComplete)
4139
					iDebugNum = lastItem;
4140
4141
				// Do we have the additional progress bar?
4142
				if (iSubStepProgress != -1)
4143
				{
4144
					document.getElementById("substep_bar_div").style.display = "";
4145
					document.getElementById("substep_bar_div2").style.display = "";
4146
					document.getElementById("substep_progress").style.width = iSubStepProgress + "%";
4147
					setInnerHTML(document.getElementById("substep_text"), iSubStepProgress + "%");
4148
					setInnerHTML(document.getElementById("substep_bar_div"), sDebugName.replace(/\./g, "") + ":");
4149
				}
4150
				else
4151
				{
4152
					document.getElementById("substep_bar_div").style.display = "none";
4153
					document.getElementById("substep_bar_div2").style.display = "none";
4154
				}
4155
4156
				// Move onto the next item?
4157
				if (bIsComplete)
4158
					lastItem = iDebugNum;
4159
				else
4160
					lastItem = iDebugNum - 1;
4161
4162
				// Are we finished?
4163
				if (bIsComplete && iDebugNum == -1 && curFile >= ', $upcontext['file_count'], ')
4164
				{';
4165
4166
		if ($is_debug)
4167
			echo '
4168
					document.getElementById(\'debug_section\').style.display = "none";
4169
4170
					var upgradeFinishedTime = parseInt(oXMLDoc.getElementsByTagName("curtime")[0].childNodes[0].nodeValue);
4171
					var diffTime = upgradeFinishedTime - upgradeStartTime;
4172
					var diffHours = Math.floor(diffTime / 3600);
4173
					var diffMinutes = parseInt((diffTime / 60) % 60);
4174
					var diffSeconds = parseInt(diffTime % 60);
4175
4176
					var totalTime = "";
4177
					if (diffHours > 0)
4178
						totalTime = totalTime + diffHours + " hour" + (diffHours > 1 ? "s" : "") + " ";
4179
					if (diffMinutes > 0)
4180
						totalTime = totalTime + diffMinutes + " minute" + (diffMinutes > 1 ? "s" : "") + " ";
4181
					if (diffSeconds > 0)
4182
						totalTime = totalTime + diffSeconds + " second" + (diffSeconds > 1 ? "s" : "");
4183
4184
					setInnerHTML(document.getElementById("upgradeCompleted"), "Completed in " + totalTime);';
4185
4186
		echo '
4187
4188
					document.getElementById(\'commess\').style.display = "";
4189
					document.getElementById(\'contbutt\').disabled = 0;
4190
					document.getElementById(\'database_done\').value = 1;';
4191
4192
		if ($upcontext['file_count'] > 1)
4193
			echo '
4194
					document.getElementById(\'info1\').style.display = "none";';
4195
4196
		echo '
4197
					document.getElementById(\'info2\').style.display = "none";
4198
					updateStepProgress(100, 100, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4199
					return true;
4200
				}
4201
				// Was it the last step in the file?
4202
				else if (bIsComplete && iDebugNum == -1)
4203
				{
4204
					lastItem = 0;
4205
					prevFile = curFile;';
4206
4207
		if ($is_debug)
4208
			echo '
4209
					setOuterHTML(document.getElementById(\'debuginfo\'), \'Moving to next script file...done<br><span id="debuginfo"><\' + \'/span>\');';
4210
4211
		echo '
4212
					getNextItem();
4213
					return true;
4214
				}';
4215
4216
		// If debug scroll the screen.
4217
		if ($is_debug)
4218
			echo '
4219
				if (iLastSubStepProgress == -1)
4220
				{
4221
					// Give it consistent dots.
4222
					dots = sDebugName.match(/\./g);
4223
					numDots = dots ? dots.length : 0;
4224
					for (var i = numDots; i < 3; i++)
4225
						sDebugName += ".";
4226
					setOuterHTML(document.getElementById(\'debuginfo\'), sDebugName + \'<span id="debuginfo"><\' + \'/span>\');
4227
				}
4228
				iLastSubStepProgress = iSubStepProgress;
4229
4230
				if (bIsComplete)
4231
					setOuterHTML(document.getElementById(\'debuginfo\'), \'done<br><span id="debuginfo"><\' + \'/span>\');
4232
				else
4233
					setOuterHTML(document.getElementById(\'debuginfo\'), \'...<span id="debuginfo"><\' + \'/span>\');
4234
4235
				if (document.getElementById(\'debug_section\').scrollHeight)
4236
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4237
4238
		echo '
4239
				// Update the page.
4240
				setInnerHTML(document.getElementById(\'item_num\'), iItemNum);
4241
				setInnerHTML(document.getElementById(\'cur_item_name\'), sItemName);';
4242
4243
		if ($upcontext['file_count'] > 1)
4244
		{
4245
			echo '
4246
				setInnerHTML(document.getElementById(\'file_done\'), curFile);
4247
				setInnerHTML(document.getElementById(\'item_count\'), totalItems);';
4248
		}
4249
4250
		echo '
4251
				// Is there an error?
4252
				if (oXMLDoc.getElementsByTagName("error")[0])
4253
				{
4254
					var sErrorMsg = "";
4255
					for (var i = 0; i < oXMLDoc.getElementsByTagName("error")[0].childNodes.length; i++)
4256
						sErrorMsg += oXMLDoc.getElementsByTagName("error")[0].childNodes[i].nodeValue;
4257
					document.getElementById("error_block").style.display = "";
4258
					setInnerHTML(document.getElementById("error_message"), sErrorMsg);
4259
					return false;
4260
				}
4261
4262
				// Get the progress bar right.
4263
				barTotal = debugItems * ', $upcontext['file_count'], ';
4264
				barDone = (debugItems * (curFile - 1)) + lastItem;
4265
4266
				updateStepProgress(barDone, barTotal, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4267
4268
				// Finally - update the time here as it shows the server is responding!
4269
				curTime = new Date();
4270
				iElapsed = (curTime.getTime() / 1000 - ', $upcontext['started'], ');
4271
				mins = parseInt(iElapsed / 60);
4272
				secs = parseInt(iElapsed - mins * 60);
4273
				setInnerHTML(document.getElementById("mins_elapsed"), mins);
4274
				setInnerHTML(document.getElementById("secs_elapsed"), secs);
4275
4276
				getNextItem();
4277
				return true;
4278
			}
4279
4280
			// What if we timeout?!
4281
			function retTimeout(attemptAgain)
4282
			{
4283
				// Oh noes...
4284
				if (!attemptAgain)
4285
				{
4286
					document.getElementById("error_block").style.display = "";
4287
					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");
4288
				}
4289
				else
4290
				{
4291
					document.getElementById("error_block").style.display = "none";
4292
					getNextItem();
4293
				}
4294
			}';
4295
4296
		// Start things off assuming we've not errored.
4297
		if (empty($upcontext['error_message']))
4298
			echo '
4299
			getNextItem();';
4300
4301
		echo '
4302
		//# sourceURL=dynamicScript-dbch.js
4303
		</script>';
4304
	}
4305
	return;
4306
}
4307
4308
function template_database_xml()
4309
{
4310
	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...
4311
4312
	echo '
4313
	<file num="', $upcontext['cur_file_num'], '" items="', $upcontext['total_items'], '" debug_items="', $upcontext['debug_items'], '">', $upcontext['cur_file_name'], '</file>
4314
	<item num="', $upcontext['current_item_num'], '">', $upcontext['current_item_name'], '</item>
4315
	<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>';
4316
4317
	if (!empty($upcontext['error_message']))
4318
		echo '
4319
	<error>', $upcontext['error_message'], '</error>';
4320
4321
	if (!empty($upcontext['error_string']))
4322
		echo '
4323
	<sql>', $upcontext['error_string'], '</sql>';
4324
4325
	if ($is_debug)
4326
		echo '
4327
	<curtime>', time(), '</curtime>';
4328
}
4329
4330
// Template for the UTF-8 conversion step. Basically a copy of the backup stuff with slight modifications....
4331
function template_convert_utf8()
4332
{
4333
	global $upcontext, $support_js, $is_debug, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

    public function myFunction() {
        // Do something
    }
}
Loading history...
4334
4335
	echo '
4336
			<h3>', $txt['upgrade_wait2'], '</h3>';
4337
4338
	echo '
4339
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4340
			<input type="hidden" name="utf8_done" id="utf8_done" value="0">
4341
			<strong>', $txt['upgrade_completed'], ' <span id="tab_done">', $upcontext['cur_table_num'], '</span> ', $txt['upgrade_outof'], ' ', $upcontext['table_count'], ' ', $txt['upgrade_tables'], '</strong>
4342
			<div id="debug_section" style="height: ', ($is_debug ? '97' : '12') , 'px; overflow: auto;">
4343
			<span id="debuginfo"></span>
4344
			</div>';
4345
4346
	// Done any tables so far?
4347 View Code Duplication
	if (!empty($upcontext['previous_tables']))
4348
		foreach ($upcontext['previous_tables'] as $table)
4349
			echo '
4350
			<br>', $txt['upgrade_completed_table'], ' &quot;', $table, '&quot;.';
4351
4352
	echo '
4353
			<h3 id="current_tab_div">', $txt['upgrade_current_table'], ' &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>';
4354
4355
	// If we dropped their index, let's let them know
4356
	if ($upcontext['dropping_index'])
4357
		echo '
4358
				<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>';
4359
4360
	// Completion notification
4361
	echo '
4362
			<br><span id="commess" style="font-weight: bold; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">', $txt['upgrade_conversion_proceed'], '</span>';
4363
4364
	// Continue please!
4365
	$upcontext['continue'] = $support_js ? 2 : 1;
4366
4367
	// If javascript allows we want to do this using XML.
4368 View Code Duplication
	if ($support_js)
4369
	{
4370
		echo '
4371
		<script>
4372
			var lastTable = ', $upcontext['cur_table_num'], ';
4373
			function getNextTables()
4374
			{
4375
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onConversionUpdate);
4376
			}
4377
4378
			// Got an update!
4379
			function onConversionUpdate(oXMLDoc)
4380
			{
4381
				var sCurrentTableName = "";
4382
				var iTableNum = 0;
4383
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4384
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4385
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4386
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4387
4388
				// Update the page.
4389
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4390
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4391
				lastTable = iTableNum;
4392
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4393
4394
		// If debug flood the screen.
4395
		if ($is_debug)
4396
			echo '
4397
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4398
4399
				if (document.getElementById(\'debug_section\').scrollHeight)
4400
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4401
4402
		echo '
4403
				// Get the next update...
4404
				if (iTableNum == ', $upcontext['table_count'], ')
4405
				{
4406
					document.getElementById(\'commess\').style.display = "";
4407
					if (document.getElementById(\'indexmsg\') != null) {
4408
						document.getElementById(\'indexmsg\').style.display = "";
4409
					}
4410
					document.getElementById(\'current_tab_div\').style.display = "none";
4411
					document.getElementById(\'contbutt\').disabled = 0;
4412
					document.getElementById(\'utf8_done\').value = 1;
4413
				}
4414
				else
4415
					getNextTables();
4416
			}
4417
			getNextTables();
4418
		//# sourceURL=dynamicScript-conv.js
4419
		</script>';
4420
	}
4421
}
4422
4423
function template_convert_xml()
4424
{
4425
	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...
4426
4427
	echo '
4428
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4429
}
4430
4431
// Template for the database backup tool/
4432
function template_serialize_json()
4433
{
4434
	global $upcontext, $support_js, $is_debug, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

    public function myFunction() {
        // Do something
    }
}
Loading history...
4435
4436
	echo '
4437
			<h3>', $txt['upgrade_convert_datajson'], '</h3>';
4438
4439
	echo '
4440
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4441
			<input type="hidden" name="json_done" id="json_done" value="0">
4442
			<strong>', $txt['upgrade_completed'], ' <span id="tab_done">', $upcontext['cur_table_num'], '</span> ', $txt['upgrade_outof'], ' ', $upcontext['table_count'], ' ', $txt['upgrade_tables'], '</strong>
4443
			<div id="debug_section" style="height: ', ($is_debug ? '115' : '12') , 'px; overflow: auto;">
4444
			<span id="debuginfo"></span>
4445
			</div>';
4446
4447
	// Dont any tables so far?
4448 View Code Duplication
	if (!empty($upcontext['previous_tables']))
4449
		foreach ($upcontext['previous_tables'] as $table)
4450
			echo '
4451
			<br>', $txt['upgrade_completed_table'], ' &quot;', $table, '&quot;.';
4452
4453
	echo '
4454
			<h3 id="current_tab_div">', $txt['upgrade_current_table'], ' &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
4455
			<br><span id="commess" style="font-weight: bold; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">', $txt['upgrade_json_completed'], '</span>';
4456
4457
	// Try to make sure substep was reset.
4458
	if ($upcontext['cur_table_num'] == $upcontext['table_count'])
4459
		echo '
4460
			<input type="hidden" name="substep" id="substep" value="0">';
4461
4462
	// Continue please!
4463
	$upcontext['continue'] = $support_js ? 2 : 1;
4464
4465
	// If javascript allows we want to do this using XML.
4466 View Code Duplication
	if ($support_js)
4467
	{
4468
		echo '
4469
		<script>
4470
			var lastTable = ', $upcontext['cur_table_num'], ';
4471
			function getNextTables()
4472
			{
4473
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4474
			}
4475
4476
			// Got an update!
4477
			function onBackupUpdate(oXMLDoc)
4478
			{
4479
				var sCurrentTableName = "";
4480
				var iTableNum = 0;
4481
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4482
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4483
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4484
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4485
4486
				// Update the page.
4487
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4488
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4489
				lastTable = iTableNum;
4490
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4491
4492
		// If debug flood the screen.
4493
		if ($is_debug)
4494
			echo '
4495
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>', $txt['upgrade_completed_table'], ' &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4496
4497
				if (document.getElementById(\'debug_section\').scrollHeight)
4498
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4499
4500
		echo '
4501
				// Get the next update...
4502
				if (iTableNum == ', $upcontext['table_count'], ')
4503
				{
4504
					document.getElementById(\'commess\').style.display = "";
4505
					document.getElementById(\'current_tab_div\').style.display = "none";
4506
					document.getElementById(\'contbutt\').disabled = 0;
4507
					document.getElementById(\'json_done\').value = 1;
4508
				}
4509
				else
4510
					getNextTables();
4511
			}
4512
			getNextTables();
4513
		//# sourceURL=dynamicScript-json.js
4514
		</script>';
4515
	}
4516
}
4517
4518
function template_serialize_json_xml()
4519
{
4520
	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...
4521
4522
	echo '
4523
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4524
}
4525
4526
function template_upgrade_complete()
4527
{
4528
	global $upcontext, $upgradeurl, $settings, $boardurl, $is_debug, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

    public function myFunction() {
        // Do something
    }
}
Loading history...
4529
4530
	echo '
4531
	<h3>', $txt['upgrade_done'], ' <a href="', $boardurl, '/index.php">', $txt['upgrade_done2'], '</a>.  ', $txt['upgrade_done2'], '</h3>
4532
	<form action="', $boardurl, '/index.php">';
4533
4534
	if (!empty($upcontext['can_delete_script']))
4535
		echo '
4536
			<label for="delete_self"><input type="checkbox" id="delete_self" onclick="doTheDelete(this);"> ', $txt['upgrade_delete_now'], '</label> ', $txt['upgrade_delete_server'], '
4537
			<script>
4538
				function doTheDelete(theCheck)
4539
				{
4540
					var theImage = document.getElementById ? document.getElementById("delete_upgrader") : document.all.delete_upgrader;
4541
4542
					theImage.src = "', $upgradeurl, '?delete=1&ts_" + (new Date().getTime());
4543
					theCheck.disabled = true;
4544
				}
4545
			</script>
4546
			<img src="', $settings['default_theme_url'], '/images/blank.png" alt="" id="delete_upgrader"><br>';
4547
4548
	$active = time() - $upcontext['started'];
4549
	$hours = floor($active / 3600);
4550
	$minutes = intval(($active / 60) % 60);
4551
	$seconds = intval($active % 60);
4552
4553
	if ($is_debug)
4554
	{
4555
		$totalTime = '';
4556
		if ($hours > 0)
4557
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
4558
		if ($minutes > 0)
4559
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
4560
		if ($seconds > 0)
4561
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
4562
	}
4563
4564
	if ($is_debug && !empty($totalTime))
4565
		echo '<br> ', $txt['upgrade_completed_time'], ' ', $totalTime, '<br><br>';
4566
4567
	echo '<br>
4568
			', $txt['upgrade_problems'], '<br>
4569
			<br>
4570
			', $txt['upgrade_luck'], '<br>
4571
			', $txt['upgrade_sm'], '';
4572
}
4573
4574
/**
4575
 * Convert MySQL (var)char ip col to binary
4576
 *
4577
 * @param string $targetTable The table to perform the operation on
4578
 * @param string $oldCol The old column to gather data from
4579
 * @param string $newCol The new column to put data in
4580
 * @param int $limit The amount of entries to handle at once.
4581
 * @param int $setSize The amount of entries after which to update the database.
4582
 *
4583
 * newCol needs to be a varbinary(16) null able field
4584
 * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

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

Loading history...
4585
 */
4586
function MySQLConvertOldIp($targetTable, $oldCol, $newCol, $limit = 50000, $setSize = 100)
4587
{
4588
	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...
4589
4590
	$current_substep = $_GET['substep'];
4591
4592
	if (empty($_GET['a']))
4593
		$_GET['a'] = 0;
4594
	$step_progress['name'] = 'Converting ips';
4595
	$step_progress['current'] = $_GET['a'];
4596
4597
	// Skip this if we don't have the column
4598
	$request = $smcFunc['db_query']('', '
4599
		SHOW FIELDS
4600
		FROM {db_prefix}{raw:table}
4601
		WHERE Field = {string:name}',
4602
		array(
4603
			'table' => $targetTable,
4604
			'name' => $oldCol,
4605
	));
4606
	if ($smcFunc['db_num_rows']($request) !== 1)
4607
	{
4608
		$smcFunc['db_free_result']($request);
4609
		return;
4610
	}
4611
	$smcFunc['db_free_result']($request);
4612
4613
	//mysql default max length is 1mb https://dev.mysql.com/doc/refman/5.1/en/packet-too-large.html
4614
	$arIp = array();
0 ignored issues
show
Unused Code introduced by
$arIp is not used, you could remove the assignment.

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

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

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

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

Loading history...
4615
4616
	$is_done = false;
4617
	while (!$is_done)
4618
	{
4619
		// Keep looping at the current step.
4620
		nextSubstep($current_substep);
4621
4622
		$arIp = array();
4623
4624
		$request = $smcFunc['db_query']('', '
4625
			SELECT DISTINCT {raw:old_col}
4626
			FROM {db_prefix}{raw:table_name}
4627
			WHERE {raw:new_col} IS NULL
4628
			LIMIT {int:limit}',
4629
			array(
4630
				'old_col' => $oldCol,
4631
				'new_col' => $newCol,
4632
				'table_name' => $targetTable,
4633
				'empty' => '',
4634
				'limit' => $limit,
4635
		));
4636
		while ($row = $smcFunc['db_fetch_assoc']($request))
4637
			$arIp[] = $row[$oldCol];
4638
		$smcFunc['db_free_result']($request);
4639
4640
		// Special case, null ip could keep us in a loop.
4641
		if (is_null($arIp[0]))
4642
			unset($arIp[0]);
4643
4644
		if (empty($arIp))
4645
			$is_done = true;
4646
4647
		$updates = array();
4648
		$cases = array();
4649
		$count = count($arIp);
4650
		for ($i = 0; $i < $count; $i++)
4651
		{
4652
			$arIp[$i] = trim($arIp[$i]);
4653
4654
			if (empty($arIp[$i]))
4655
				continue;
4656
4657
			$updates['ip' . $i] = $arIp[$i];
4658
			$cases[$arIp[$i]] = 'WHEN ' . $oldCol . ' = {string:ip' . $i . '} THEN {inet:ip' . $i . '}';
4659
4660
			if ($setSize > 0 && $i % $setSize === 0)
4661
			{
4662
				if (count($updates) == 1)
4663
					continue;
4664
4665
				$updates['whereSet'] = array_values($updates);
4666
				$smcFunc['db_query']('', '
4667
					UPDATE {db_prefix}' . $targetTable . '
4668
					SET ' . $newCol . ' = CASE ' .
4669
					implode('
4670
						', $cases) . '
4671
						ELSE NULL
4672
					END
4673
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4674
					$updates
4675
				);
4676
4677
				$updates = array();
4678
				$cases = array();
4679
			}
4680
		}
4681
4682
		// Incase some extras made it through.
4683
		if (!empty($updates))
4684
		{
4685
			if (count($updates) == 1)
4686
			{
4687
				foreach ($updates as $key => $ip)
4688
				{
4689
					$smcFunc['db_query']('', '
4690
						UPDATE {db_prefix}' . $targetTable . '
4691
						SET ' . $newCol . ' = {inet:ip}
4692
						WHERE ' . $oldCol . ' = {string:ip}',
4693
						array(
4694
							'ip' => $ip
4695
					));
4696
				}
4697
			}
4698
			else
4699
			{
4700
				$updates['whereSet'] = array_values($updates);
4701
				$smcFunc['db_query']('', '
4702
					UPDATE {db_prefix}' . $targetTable . '
4703
					SET ' . $newCol . ' = CASE ' .
4704
					implode('
4705
						', $cases) . '
4706
						ELSE NULL
4707
					END
4708
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4709
					$updates
4710
				);
4711
			}
4712
		}
4713
		else
4714
			$is_done = true;
4715
4716
		$_GET['a'] += $limit;
4717
		$step_progress['current'] = $_GET['a'];
4718
	}
4719
4720
	unset($_GET['a']);
4721
}
4722
4723
/**
4724
 * Get the column info. This is basically the same as smf_db_list_columns but we get 1 column, force detail and other checks.
4725
 *
4726
 * @param string $targetTable The table to perform the operation on
4727
 * @param string $column The column we are looking for.
4728
 *
4729
 * @return array Info on the table.
4730
 */
4731
function upgradeGetColumnInfo($targetTable, $column)
4732
{
4733
 	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...
4734
4735
 	// This should already be here, but be safe.
4736
 	db_extend('packages');
4737
4738
 	$columns = $smcFunc['db_list_columns']($targetTable, true);
4739
4740
	if (isset($columns[$column]))
4741
		return $columns[$column];
4742
	else
4743
		return null;
4744
}
4745
4746
?>