Completed
Pull Request — release-2.1 (#4844)
by Jeremy
12:48 queued 03:57
created

upgrade.php ➔ detectSettingsFileMigrationNeeded()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

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

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

    public function myFunction() {
        // Do something
    }
}
Loading history...
84
85
// Initialize everything and load the language files.
86
initialize_inputs();
87
load_lang_file();
88
89
90
// All the steps in detail.
91
// Number,Name,Function,Progress Weight.
92
$upcontext['steps'] = array(
93
	0 => array(1, $txt['upgrade_step_login'], 'WelcomeLogin', 2),
94
	1 => array(2, $txt['upgrade_step_options'], 'UpgradeOptions', 2),
95
	2 => array(3, $txt['upgrade_step_backup'], 'BackupDatabase', 10),
96
	3 => array(4, $txt['upgrade_step_database'], 'DatabaseChanges', 50),
97
	4 => array(5, $txt['upgrade_step_convertutf'], 'ConvertUtf8', 20),
98
	5 => array(6, $txt['upgrade_step_convertjson'], 'serialize_to_json', 10),
99
	6 => array(7, $txt['upgrade_step_delete'], 'DeleteUpgrade', 1),
100
);
101
// Just to remember which one has files in it.
102
$upcontext['database_step'] = 3;
103
@set_time_limit(600);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

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

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

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

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

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

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

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

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

Loading history...
328
	}
329
330
	// Handle the progress of the step, if any.
331
	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
332
	{
333
		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
334
		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
335
	}
336
	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
337
338
	// We usually dump our templates out.
339
	if (!$fallThrough)
340
	{
341
		// This should not happen my dear... HELP ME DEVELOPERS!!
342
		if (!empty($command_line))
343
		{
344
			if (function_exists('debug_print_backtrace'))
345
				debug_print_backtrace();
346
347
			echo "\n" . 'Error: Unexpected call to use the ' . (isset($upcontext['sub_template']) ? $upcontext['sub_template'] : '') . ' template. Please copy and paste all the text above and visit the SMF support forum to tell the Developers that they\'ve made a boo boo; they\'ll get you up and running again.';
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 321
  2. $upcontext is assigned
    in other/upgrade.php on line 322
  3. $upcontext is assigned
    in other/upgrade.php on line 323
  4. $upcontext is assigned
    in other/upgrade.php on line 336

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...
348
			flush();
349
			die();
350
		}
351
352
		if (!isset($_GET['xml']))
353
			template_upgrade_above();
354
		else
355
		{
356
			header('content-type: text/xml; charset=UTF-8');
357
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
358
			$upcontext['get_data'] = array();
359
			foreach ($_GET as $k => $v)
360
			{
361
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
362
				{
363
					$upcontext['get_data'][$k] = $v;
364
				}
365
			}
366
			template_xml_above();
367
		}
368
369
		// Call the template.
370
		if (isset($upcontext['sub_template']))
371
		{
372
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
373
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(json_encode($upcontext['upgrade_status']));
374
375
			// Custom stuff to pass back?
376
			if (!empty($upcontext['query_string']))
377
				$upcontext['form_url'] .= $upcontext['query_string'];
378
379
			// Call the appropriate subtemplate
380
			if (is_callable('template_' . $upcontext['sub_template']))
381
				call_user_func('template_' . $upcontext['sub_template']);
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 321
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 321
  2. $upcontext is assigned
    in other/upgrade.php on line 322
  3. $upcontext is assigned
    in other/upgrade.php on line 323
  4. $upcontext is assigned
    in other/upgrade.php on line 336
  5. $upcontext is assigned
    in other/upgrade.php on line 372
  6. $upcontext is assigned
    in other/upgrade.php on line 373
  2. Path: Read from $_GET, and $v is assigned in other/upgrade.php on line 359
  1. Read from $_GET, and $v is assigned
    in other/upgrade.php on line 359
  2. $upcontext is assigned
    in other/upgrade.php on line 363
  3. $upcontext is assigned
    in other/upgrade.php on line 372
  4. $upcontext is assigned
    in other/upgrade.php on line 373
  3. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 373
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 373

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...
382
			else
383
				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 321
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 321
  2. $upcontext is assigned
    in other/upgrade.php on line 322
  3. $upcontext is assigned
    in other/upgrade.php on line 323
  4. $upcontext is assigned
    in other/upgrade.php on line 336
  5. $upcontext is assigned
    in other/upgrade.php on line 372
  6. $upcontext is assigned
    in other/upgrade.php on line 373
  2. Path: Read from $_GET, and $v is assigned in other/upgrade.php on line 359
  1. Read from $_GET, and $v is assigned
    in other/upgrade.php on line 359
  2. $upcontext is assigned
    in other/upgrade.php on line 363
  3. $upcontext is assigned
    in other/upgrade.php on line 372
  4. $upcontext is assigned
    in other/upgrade.php on line 373
  3. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 373
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 373

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...
384
		}
385
386
		// Was there an error?
387
		if (!empty($upcontext['forced_error_message']))
388
			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 321
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 321
  2. $upcontext is assigned
    in other/upgrade.php on line 322
  3. $upcontext is assigned
    in other/upgrade.php on line 323
  4. $upcontext is assigned
    in other/upgrade.php on line 336
  2. Path: Read from $_GET, and $v is assigned in other/upgrade.php on line 359
  1. Read from $_GET, and $v is assigned
    in other/upgrade.php on line 359
  2. $upcontext is assigned
    in other/upgrade.php on line 363
  3. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 373
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 373

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...
389
390
		// Show the footer.
391
		if (!isset($_GET['xml']))
392
			template_upgrade_below();
393
		else
394
			template_xml_below();
395
	}
396
397
398
	if (!empty($command_line) && $is_debug)
399
	{
400
		$active = time() - $upcontext['started'];
401
		$hours = floor($active / 3600);
402
		$minutes = intval(($active / 60) % 60);
403
		$seconds = intval($active % 60);
404
405
		$totalTime = '';
406
		if ($hours > 0)
407
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
408
		if ($minutes > 0)
409
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
410
		if ($seconds > 0)
411
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
412
413
		if (!empty($totalTime))
414
			echo "\n" . '', $txt['upgrade_completed_time'], ' ' . $totalTime . "\n";
415
	}
416
417
	// Bang - gone!
418
	die();
419
}
420
421
// Load the list of language files, and the current language file.
422
function load_lang_file()
0 ignored issues
show
Best Practice introduced by
The function load_lang_file() has been defined more than once; this definition is ignored, only the first definition in other/install.php (L267-338) is considered.

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

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

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

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
423
{
424
	global $txt, $incontext, $user_info;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

    public function myFunction() {
        // Do something
    }
}
Loading history...
425
426
	$incontext['detected_languages'] = array();
427
428
	// Make sure the languages directory actually exists.
429 View Code Duplication
	if (file_exists(dirname(__FILE__) . '/Themes/default/languages'))
430
	{
431
		// Find all the "Install" language files in the directory.
432
		$dir = dir(dirname(__FILE__) . '/Themes/default/languages');
433
		while ($entry = $dir->read())
434
		{
435
			if (substr($entry, 0, 8) == 'Install.' && substr($entry, -4) == '.php')
436
				$incontext['detected_languages'][$entry] = ucfirst(substr($entry, 8, strlen($entry) - 12));
437
		}
438
		$dir->close();
439
	}
440
441
	// Didn't find any, show an error message!
442 View Code Duplication
	if (empty($incontext['detected_languages']))
443
	{
444
		// Let's not cache this message, eh?
445
		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
446
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
447
		header('Cache-Control: no-cache');
448
449
		echo '<!DOCTYPE html>
450
<html>
451
	<head>
452
		<title>SMF Upgrader: Error!</title>
453
	</head>
454
	<body style="font-family: sans-serif;"><div style="width: 600px;">
455
		<h1 style="font-size: 14pt;">A critical error has occurred.</h1>
456
457
		<p>This upgrader was unable to find the upgrader\'s language file or files.  They should be found under:</p>
458
459
		<div style="margin: 1ex; font-family: monospace; font-weight: bold;">', dirname($_SERVER['PHP_SELF']) != '/' ? dirname($_SERVER['PHP_SELF']) : '', '/Themes/default/languages</div>
0 ignored issues
show
Security Cross-Site Scripting introduced by
dirname($_SERVER['PHP_SE...ERVER['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, and $_SERVER['PHP_SELF'] is passed through dirname()
    in other/upgrade.php on line 459

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...
460
461
		<p>In some cases, FTP clients do not properly upload files with this many folders.  Please double check to make sure you <span style="font-weight: 600;">have uploaded all the files in the distribution</span>.</p>
462
		<p>If that doesn\'t help, please make sure this install.php file is in the same place as the Themes folder.</p>
463
464
		<p>If you continue to get this error message, feel free to <a href="https://support.simplemachines.org/">look to us for support</a>.</p>
465
	</div></body>
466
</html>';
467
		die;
468
	}
469
470
	// Override the language file?
471 View Code Duplication
	if (isset($_GET['lang_file']))
472
		$_SESSION['installer_temp_lang'] = $_GET['lang_file'];
473
	elseif (isset($GLOBALS['HTTP_GET_VARS']['lang_file']))
474
		$_SESSION['installer_temp_lang'] = $GLOBALS['HTTP_GET_VARS']['lang_file'];
475
476
	// Make sure it exists, if it doesn't reset it.
477
	if (!isset($_SESSION['installer_temp_lang']) || preg_match('~[^\\w_\\-.]~', $_SESSION['installer_temp_lang']) === 1 || !file_exists(dirname(__FILE__) . '/Themes/default/languages/' . $_SESSION['installer_temp_lang']))
478
	{
479
		// Use the first one...
480
		list ($_SESSION['installer_temp_lang']) = array_keys($incontext['detected_languages']);
481
482
		// If we have english and some other language, use the other language.  We Americans hate english :P.
483 View Code Duplication
		if ($_SESSION['installer_temp_lang'] == 'Install.english.php' && count($incontext['detected_languages']) > 1)
484
			list (, $_SESSION['installer_temp_lang']) = array_keys($incontext['detected_languages']);
485
486
		// For backup we load the english at first -> second language overwrite the english one
487
		if (count($incontext['detected_languages']) > 1)
488
			require_once(dirname(__FILE__) . '/Themes/default/languages/Install.english.php');
489
	}
490
491
	// And now include the actual language file itself.
492
	require_once(dirname(__FILE__) . '/Themes/default/languages/' . $_SESSION['installer_temp_lang']);
493
494
	// Which language did we load? Assume that he likes his language.
495
	preg_match('~^Install\.(.+[^-utf8])\.php$~', $_SESSION['installer_temp_lang'], $matches);
496
	if (empty($matches[1]))
497
		$matches = [
498
			0 => 'nothing',
499
			1 => 'english',
500
		];
501
	$user_info['language'] = $matches[1];
502
}
503
504
// Used to direct the user to another location.
505
function redirectLocation($location, $addForm = true)
506
{
507
	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...
508
509
	// Command line users can't be redirected.
510
	if ($command_line)
511
		upgradeExit(true);
512
513
	// Are we providing the core info?
514
	if ($addForm)
515
	{
516
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
517
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
518
	}
519
520
	while (@ob_end_clean());
521
	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 517
  2. $location is passed through strtr()
    in other/upgrade.php on line 521

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...
522
523
	// Exit - saving status as we go.
524
	upgradeExit(true);
525
}
526
527
// Load all essential data and connect to the DB as this is pre SSI.php
528
function loadEssentialData()
529
{
530
	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...
531
	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...
532
533
	error_reporting(E_ALL);
534
	define('SMF', 1);
535
536
	// Start the session.
537
	if (@ini_get('session.save_handler') == 'user')
538
		@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...
539
	@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...
540
541
	if (empty($smcFunc))
542
		$smcFunc = array();
543
544
	// We need this for authentication and some upgrade code
545
	require_once($sourcedir . '/Subs-Auth.php');
546
	require_once($sourcedir . '/Class-Package.php');
547
548
	$smcFunc['strtolower'] = 'smf_strtolower';
549
550
	// Initialize everything...
551
	initialize_inputs();
552
553
	// Get the database going!
554
	if (empty($db_type) || $db_type == 'mysqli')
555
	{
556
		$db_type = 'mysql';
557
		// If overriding $db_type, need to set its settings.php entry too
558
		$changes = array();
559
		$changes['db_type'] = '\'mysql\'';
560
		require_once($sourcedir . '/Subs-Admin.php');
561
		updateSettingsFile($changes);
562
	}
563
564
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
565
	{
566
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
567
568
		// Make the connection...
569
		if (empty($db_connection))
570
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, array('non_fatal' => true));
571
		else
572
			// If we've returned here, ping/reconnect to be safe
573
			$smcFunc['db_ping']($db_connection);
574
575
		// Oh dear god!!
576
		if ($db_connection === null)
577
			die('Unable to connect to database - please check username and password are correct in Settings.php');
578
579
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
580
			$smcFunc['db_query']('', '
581
			SET NAMES {string:db_character_set}',
582
			array(
583
				'db_error_skip' => true,
584
				'db_character_set' => $db_character_set,
585
			)
586
		);
587
588
		// Load the modSettings data...
589
		$request = $smcFunc['db_query']('', '
590
			SELECT variable, value
591
			FROM {db_prefix}settings',
592
			array(
593
				'db_error_skip' => true,
594
			)
595
		);
596
		$modSettings = array();
597 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
598
			$modSettings[$row['variable']] = $row['value'];
599
		$smcFunc['db_free_result']($request);
600
	}
601
	else
602
	{
603
		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.');
604
	}
605
606
	require_once($sourcedir . '/Subs.php');
607
608
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
609
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
610
	{
611
		require_once($sourcedir . '/QueryString.php');
612
		cleanRequest();
613
	}
614
615
	if (!isset($_GET['substep']))
616
		$_GET['substep'] = 0;
617
}
618
619
function initialize_inputs()
620
{
621
	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...
622
623
	$start_time = time();
624
625
	umask(0);
626
627
	ob_start();
628
629
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
630
	ignore_user_abort(true);
631
632
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
633
	if (isset($_GET['delete']))
634
	{
635
		@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...
636
637
		// And the extra little files ;).
638
		@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...
639
		@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...
640
		@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...
641
		@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...
642
		@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...
643
644
		$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...
645
		while ($file = readdir($dh))
646
		{
647
			if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
648
				@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...
649
		}
650
		closedir($dh);
651
652
		// Legacy files while we're at it. NOTE: We only touch files we KNOW shouldn't be there.
653
		// 1.1 Sources files not in 2.0+
654
		@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...
655
		// 1.1 Templates that don't exist any more (e.g. renamed)
656
		@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...
657
		@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...
658
		// 1.1 JS files were stored in the main theme folder, but in 2.0+ are in the scripts/ folder
659
		@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...
660
		@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...
661
		@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...
662
		@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...
663
		@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...
664
665
		// 2.0 Sources files not in 2.1+
666
		@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...
667
		@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...
668
669
		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 669

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...
670
		exit;
671
	}
672
673
	// Something is causing this to happen, and it's annoying.  Stop it.
674
	$temp = 'upgrade_php?step';
675
	while (strlen($temp) > 4)
676
	{
677
		if (isset($_GET[$temp]))
678
			unset($_GET[$temp]);
679
		$temp = substr($temp, 1);
680
	}
681
682
	// Force a step, defaulting to 0.
683
	$_GET['step'] = (int) @$_GET['step'];
684
	$_GET['substep'] = (int) @$_GET['substep'];
685
}
686
687
// Step 0 - Let's welcome them in and ask them to login!
688
function WelcomeLogin()
689
{
690
	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...
691
	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...
692
693
	// We global $txt here so that the language files can add to them. This variable is NOT unused.
694
	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...
695
696
	$upcontext['sub_template'] = 'welcome_message';
697
698
	// Check for some key files - one template, one language, and a new and an old source file.
699
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
700
		&& @file_exists($sourcedir . '/QueryString.php')
701
		&& @file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')
702
		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
703
704
	// Need legacy scripts?
705 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.1)
706
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
707 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.0)
708
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-1.sql');
709 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 1.1)
710
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
711
712
	// We don't need "-utf8" files anymore...
713
	$upcontext['language'] = str_ireplace('-utf8', '', $upcontext['language']);
714
715
	// This needs to exist!
716
	if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
717
		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>]');
718
	else
719
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
720
721
	if (!$check)
722
		// 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.
723
		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.');
724
725
	// Do they meet the install requirements?
726
	if (!php_version_check())
727
		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.');
728
729
	if (!db_version_check())
730
		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.');
731
732
	// Do some checks to make sure they have proper privileges
733
	db_extend('packages');
734
735
	// CREATE
736
	$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');
737
738
	// ALTER
739
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
740
741
	// DROP
742
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
743
744
	// Sorry... we need CREATE, ALTER and DROP
745 View Code Duplication
	if (!$create || !$alter || !$drop)
746
		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.');
747
748
	// Do a quick version spot check.
749
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
750
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
751
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
752
		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.');
753
754
	// What absolutely needs to be writable?
755
	$writable_files = array(
756
		$boarddir . '/Settings.php',
757
		$boarddir . '/Settings_bak.php',
758
	);
759
760
	// Only check for minified writable files if we have it enabled or not set.
761
	if (!empty($modSettings['minimize_files']) || !isset($modSettings['minimize_files']))
762
		$writable_files += array(
763
			$modSettings['theme_dir'] . '/css/minified.css',
764
			$modSettings['theme_dir'] . '/scripts/minified.js',
765
			$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
766
		);
767
768
	// Do we need to add this setting?
769
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
770
771
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] . '/custom_avatar';
772
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
773
774
	// This little fellow has to cooperate...
775
	quickFileWritable($custom_av_dir);
776
777
	// Are we good now?
778
	if (!is_writable($custom_av_dir))
779
		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));
780
	elseif ($need_settings_update)
781
	{
782
		if (!function_exists('cache_put_data'))
783
			require_once($sourcedir . '/Load.php');
784
785
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
786
		updateSettings(array('custom_avatar_url' => $custom_av_url));
787
	}
788
789
	require_once($sourcedir . '/Security.php');
790
791
	// Check the cache directory.
792
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
793
	if (!file_exists($cachedir_temp))
794
		@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...
795
796
	if (!file_exists($cachedir_temp))
797
		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.');
798
799
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
800
		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>.');
801
	elseif (!isset($_GET['skiplang']))
802
	{
803
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
804
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
805
806
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
807
			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>]');
808
	}
809
810
	if (!makeFilesWritable($writable_files))
811
		return false;
812
813
	// Check agreement.txt. (it may not exist, in which case $boarddir must be writable.)
814 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
815
		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.');
816
817
	// Upgrade the agreement.
818
	elseif (isset($modSettings['agreement']))
819
	{
820
		$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...
821
		fwrite($fp, $modSettings['agreement']);
822
		fclose($fp);
823
	}
824
825
	// We're going to check that their board dir setting is right in case they've been moving stuff around.
826
	if (strtr($boarddir, array('/' => '', '\\' => '')) != strtr(dirname(__FILE__), array('/' => '', '\\' => '')))
827
		$upcontext['warning'] = '
828
			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>
829
			<ul>
830
				<li>Board Directory: ' . $boarddir . '</li>
831
				<li>Source Directory: ' . $boarddir . '</li>
832
				<li>Cache Directory: ' . $cachedir_temp . '</li>
833
			</ul>
834
			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.';
835
836
	// Confirm mbstring is loaded...
837
	if (!extension_loaded('mbstring'))
838
		return throw_error($txt['install_no_mbstring']);
839
840
	// Check for https stream support.
841
	$supported_streams = stream_get_wrappers();
842
	if (!in_array('https', $supported_streams))
843
		$upcontext['custom_warning'] = $txt['install_no_https'];
844
845
	// Either we're logged in or we're going to present the login.
846
	if (checkLogin())
847
		return true;
848
849
	$upcontext += createToken('login');
850
851
	return false;
852
}
853
854
// Step 0.5: Does the login work?
855
function checkLogin()
856
{
857
	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...
858
	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...
859
860
	// Don't bother if the security is disabled.
861
	if ($disable_security)
862
		return true;
863
864
	// Are we trying to login?
865
	if (isset($_POST['contbutt']) && (!empty($_POST['user'])))
866
	{
867
		// If we've disabled security pick a suitable name!
868
		if (empty($_POST['user']))
869
			$_POST['user'] = 'Administrator';
870
871
		// Before 2.0 these column names were different!
872
		$oldDB = false;
873
		if (empty($db_type) || $db_type == 'mysql')
874
		{
875
			$request = $smcFunc['db_query']('', '
876
				SHOW COLUMNS
877
				FROM {db_prefix}members
878
				LIKE {string:member_name}',
879
				array(
880
					'member_name' => 'memberName',
881
					'db_error_skip' => true,
882
				)
883
			);
884
			if ($smcFunc['db_num_rows']($request) != 0)
885
				$oldDB = true;
886
			$smcFunc['db_free_result']($request);
887
		}
888
889
		// Get what we believe to be their details.
890
		if (!$disable_security)
891
		{
892
			if ($oldDB)
893
				$request = $smcFunc['db_query']('', '
894
					SELECT id_member, memberName AS member_name, passwd, id_group,
895
					additionalGroups AS additional_groups, lngfile
896
					FROM {db_prefix}members
897
					WHERE memberName = {string:member_name}',
898
					array(
899
						'member_name' => $_POST['user'],
900
						'db_error_skip' => true,
901
					)
902
				);
903
			else
904
				$request = $smcFunc['db_query']('', '
905
					SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile
906
					FROM {db_prefix}members
907
					WHERE member_name = {string:member_name}',
908
					array(
909
						'member_name' => $_POST['user'],
910
						'db_error_skip' => true,
911
					)
912
				);
913
			if ($smcFunc['db_num_rows']($request) != 0)
914
			{
915
				list ($id_member, $name, $password, $id_group, $addGroups, $user_language) = $smcFunc['db_fetch_row']($request);
916
917
				$groups = explode(',', $addGroups);
918
				$groups[] = $id_group;
919
920
				foreach ($groups as $k => $v)
921
					$groups[$k] = (int) $v;
922
923
				$sha_passwd = sha1(strtolower($name) . un_htmlspecialchars($_REQUEST['passwrd']));
924
925
				// We don't use "-utf8" anymore...
926
				$user_language = str_ireplace('-utf8', '', $user_language);
927
			}
928
			else
929
				$upcontext['username_incorrect'] = true;
930
931
			$smcFunc['db_free_result']($request);
932
		}
933
		$upcontext['username'] = $_POST['user'];
934
935
		// Track whether javascript works!
936
		if (!empty($_POST['js_works']))
937
		{
938
			$upcontext['upgrade_status']['js'] = 1;
939
			$support_js = 1;
940
		}
941
		else
942
			$support_js = 0;
943
944
		// Note down the version we are coming from.
945
		if (!empty($modSettings['smfVersion']) && empty($upcontext['user']['version']))
946
			$upcontext['user']['version'] = $modSettings['smfVersion'];
947
948
		// Didn't get anywhere?
949
		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']))
950
		{
951
			// MD5?
952
			$md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user']));
953
			if ($md5pass != $password)
954
			{
955
				$upcontext['password_failed'] = true;
956
				// Disable the hashing this time.
957
				$upcontext['disable_login_hashing'] = true;
958
			}
959
		}
960
961
		if ((empty($upcontext['password_failed']) && !empty($name)) || $disable_security)
962
		{
963
			// Set the password.
964
			if (!$disable_security)
965
			{
966
				// Do we actually have permission?
967
				if (!in_array(1, $groups))
968
				{
969
					$request = $smcFunc['db_query']('', '
970
						SELECT permission
971
						FROM {db_prefix}permissions
972
						WHERE id_group IN ({array_int:groups})
973
							AND permission = {string:admin_forum}',
974
						array(
975
							'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...
976
							'admin_forum' => 'admin_forum',
977
							'db_error_skip' => true,
978
						)
979
					);
980
					if ($smcFunc['db_num_rows']($request) == 0)
981
						return throw_error('You need to be an admin to perform an upgrade!');
982
					$smcFunc['db_free_result']($request);
983
				}
984
985
				$upcontext['user']['id'] = $id_member;
986
				$upcontext['user']['name'] = $name;
987
			}
988
			else
989
			{
990
				$upcontext['user']['id'] = 1;
991
				$upcontext['user']['name'] = 'Administrator';
992
			}
993
			$upcontext['user']['pass'] = mt_rand(0, 60000);
994
			// This basically is used to match the GET variables to Settings.php.
995
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
996
997
			// Set the language to that of the user?
998
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
999
			{
1000
				$user_language = basename($user_language, '.lng');
1001
				$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
1002
				preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
1003
1004
				if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
1005
					$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'] . '.';
1006
				elseif (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . basename($user_language, '.lng') . '.php'))
1007
					$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'] . '.';
1008
				else
1009
				{
1010
					// Set this as the new language.
1011
					$upcontext['language'] = $user_language;
1012
					$upcontext['upgrade_status']['lang'] = $upcontext['language'];
1013
1014
					// Include the file.
1015
					require_once($modSettings['theme_dir'] . '/languages/Install.' . $user_language . '.php');
1016
				}
1017
			}
1018
1019
			// If we're resuming set the step and substep to be correct.
1020
			if (isset($_POST['cont']))
1021
			{
1022
				$upcontext['current_step'] = $upcontext['user']['step'];
1023
				$_GET['substep'] = $upcontext['user']['substep'];
1024
			}
1025
1026
			return true;
1027
		}
1028
	}
1029
1030
	return false;
1031
}
1032
1033
// Step 1: Do the maintenance and backup.
1034
function UpgradeOptions()
1035
{
1036
	global $db_prefix, $command_line, $modSettings, $is_debug, $smcFunc, $packagesdir, $tasksdir, $language, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

    public function myFunction() {
        // Do something
    }
}
Loading history...
1037
	global $boarddir, $boardurl, $sourcedir, $maintenance, $cachedir, $upcontext, $db_type, $db_server, $image_proxy_enabled;
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...
1038
1039
	$upcontext['sub_template'] = 'upgrade_options';
1040
	$upcontext['page_title'] = $txt['upgrade_options'];
1041
1042
	db_extend('packages');
1043
	$upcontext['karma_installed'] = array('good' => false, 'bad' => false);
1044
	$member_columns = $smcFunc['db_list_columns']('{db_prefix}members');
1045
1046
	$upcontext['karma_installed']['good'] = in_array('karma_good', $member_columns);
1047
	$upcontext['karma_installed']['bad'] = in_array('karma_bad', $member_columns);
1048
1049
	unset($member_columns);
1050
1051
	// If these options are missing, we may need to migrate to a new Settings.php
1052
	$upcontext['migrateSettingsNeeded'] = detectSettingsFileMigrationNeeded() ? 1 : 0;
1053
1054
	// If we've not submitted then we're done.
1055
	if (empty($_POST['upcont']))
1056
		return false;
1057
1058
	// Firstly, if they're enabling SM stat collection just do it.
1059
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']))
1060
	{
1061
		$upcontext['allow_sm_stats'] = true;
1062
1063
		// Don't register if we still have a key.
1064
		if (empty($modSettings['sm_stats_key']))
1065
		{
1066
			// Attempt to register the site etc.
1067
			$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...
1068 View Code Duplication
			if ($fp)
1069
			{
1070
				$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
1071
				$out .= 'Host: www.simplemachines.org' . "\r\n";
1072
				$out .= 'Connection: Close' . "\r\n\r\n";
1073
				fwrite($fp, $out);
1074
1075
				$return_data = '';
1076
				while (!feof($fp))
1077
					$return_data .= fgets($fp, 128);
1078
1079
				fclose($fp);
1080
1081
				// Get the unique site ID.
1082
				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...
1083
1084
				if (!empty($ID[1]))
1085
					$smcFunc['db_insert']('replace',
1086
						$db_prefix . 'settings',
1087
						array('variable' => 'string', 'value' => 'string'),
1088
						array(
1089
							array('sm_stats_key', $ID[1]),
1090
							array('enable_sm_stats', 1),
1091
						),
1092
						array('variable')
1093
					);
1094
			}
1095
		}
1096
		else
1097
		{
1098
			$smcFunc['db_insert']('replace',
1099
				$db_prefix . 'settings',
1100
				array('variable' => 'string', 'value' => 'string'),
1101
				array('enable_sm_stats', 1),
1102
				array('variable')
1103
			);
1104
		}
1105
	}
1106
	// Don't remove stat collection unless we unchecked the box for real, not from the loop.
1107 View Code Duplication
	elseif (empty($_POST['stats']) && empty($upcontext['allow_sm_stats']))
1108
		$smcFunc['db_query']('', '
1109
			DELETE FROM {db_prefix}settings
1110
			WHERE variable = {string:enable_sm_stats}',
1111
			array(
1112
				'enable_sm_stats' => 'enable_sm_stats',
1113
				'db_error_skip' => true,
1114
			)
1115
		);
1116
1117
	// Deleting old karma stuff?
1118
	if (!empty($_POST['delete_karma']))
1119
	{
1120
		// Delete old settings vars.
1121
		$smcFunc['db_query']('', '
1122
			DELETE FROM {db_prefix}settings
1123
			WHERE variable IN ({array_string:karma_vars})',
1124
			array(
1125
				'karma_vars' => array('karmaMode', 'karmaTimeRestrictAdmins', 'karmaWaitTime', 'karmaMinPosts', 'karmaLabel', 'karmaSmiteLabel', 'karmaApplaudLabel'),
1126
			)
1127
		);
1128
1129
		// Cleaning up old karma member settings.
1130
		if ($upcontext['karma_installed']['good'])
1131
			$smcFunc['db_query']('', '
1132
				ALTER TABLE {db_prefix}members
1133
				DROP karma_good',
1134
				array()
1135
			);
1136
1137
		// Does karma bad was enable?
1138
		if ($upcontext['karma_installed']['bad'])
1139
			$smcFunc['db_query']('', '
1140
				ALTER TABLE {db_prefix}members
1141
				DROP karma_bad',
1142
				array()
1143
			);
1144
1145
		// Cleaning up old karma permissions.
1146
		$smcFunc['db_query']('', '
1147
			DELETE FROM {db_prefix}permissions
1148
			WHERE permission = {string:karma_vars}',
1149
			array(
1150
				'karma_vars' => 'karma_edit',
1151
			)
1152
		);
1153
		// Cleaning up old log_karma table
1154
		$smcFunc['db_query']('', '
1155
			DROP TABLE IF EXISTS {db_prefix}log_karma',
1156
			array()
1157
		);
1158
	}
1159
1160
	// Emptying the error log?
1161
	if (!empty($_POST['empty_error']))
1162
		$smcFunc['db_query']('truncate_table', '
1163
			TRUNCATE {db_prefix}log_errors',
1164
			array(
1165
			)
1166
		);
1167
1168
	$changes = array();
1169
1170
	// Add proxy settings.
1171
	if (!isset($GLOBALS['image_proxy_maxsize']))
1172
		$changes += array(
1173
			'image_proxy_secret' => '\'' . substr(sha1(mt_rand()), 0, 20) . '\'',
1174
			'image_proxy_maxsize' => 5190,
1175
			'image_proxy_enabled' => 0,
1176
		);
1177
1178
	// If $boardurl reflects https, set force_ssl
1179
	if (!function_exists('cache_put_data'))
1180
		require_once($sourcedir . '/Load.php');
1181
	if (stripos($boardurl, 'https://') !== false)
1182
		updateSettings(array('force_ssl' => '1'));
1183
1184
	// If we're overriding the language follow it through.
1185
	if (isset($_GET['lang']) && file_exists($modSettings['theme_dir'] . '/languages/index.' . $_GET['lang'] . '.php'))
1186
		$changes['language'] = '\'' . $_GET['lang'] . '\'';
1187
1188
	if (!empty($_POST['maint']))
1189
	{
1190
		$changes['maintenance'] = '2';
1191
		// Remember what it was...
1192
		$upcontext['user']['main'] = $maintenance;
1193
1194
		if (!empty($_POST['maintitle']))
1195
		{
1196
			$changes['mtitle'] = '\'' . addslashes($_POST['maintitle']) . '\'';
1197
			$changes['mmessage'] = '\'' . addslashes($_POST['mainmessage']) . '\'';
1198
		}
1199
		else
1200
		{
1201
			$changes['mtitle'] = '\'Upgrading the forum...\'';
1202
			$changes['mmessage'] = '\'Don\\\'t worry, we will be back shortly with an updated forum.  It will only be a minute ;).\'';
1203
		}
1204
	}
1205
1206
	if ($command_line)
1207
		echo ' * Updating Settings.php...';
1208
1209
	// Fix some old paths.
1210
	if (substr($boarddir, 0, 1) == '.')
1211
		$changes['boarddir'] = '\'' . fixRelativePath($boarddir) . '\'';
1212
1213
	if (substr($sourcedir, 0, 1) == '.')
1214
		$changes['sourcedir'] = '\'' . fixRelativePath($sourcedir) . '\'';
1215
1216
	if (empty($cachedir) || substr($cachedir, 0, 1) == '.')
1217
		$changes['cachedir'] = '\'' . fixRelativePath($boarddir) . '/cache\'';
1218
1219
	// If they have a "host:port" setup for the host, split that into separate values
1220
	// You should never have a : in the hostname if you're not on MySQL, but better safe than sorry
1221
	if (strpos($db_server, ':') !== false && $db_type == 'mysql')
1222
	{
1223
		list ($db_server, $db_port) = explode(':', $db_server);
1224
1225
		$changes['db_server'] = '\'' . $db_server . '\'';
1226
1227
		// Only set this if we're not using the default port
1228
		if ($db_port != ini_get('mysqli.default_port'))
1229
			$changes['db_port'] = (int) $db_port;
1230
	}
1231
	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...
1232
	{
1233
		// If db_port is set and is the same as the default, set it to ''
1234
		if ($db_type == 'mysql')
1235
		{
1236
			if ($db_port == ini_get('mysqli.default_port'))
1237
				$changes['db_port'] = '\'\'';
1238
			elseif ($db_type == 'postgresql' && $db_port == 5432)
1239
				$changes['db_port'] = '\'\'';
1240
		}
1241
	}
1242
1243
	// Maybe we haven't had this option yet?
1244
	if (empty($packagesdir))
1245
		$changes['packagesdir'] = '\'' . fixRelativePath($boarddir) . '/Packages\'';
1246
1247
	// Add support for $tasksdir var.
1248
	if (empty($tasksdir))
1249
		$changes['tasksdir'] = '\'' . fixRelativePath($sourcedir) . '/tasks\'';
1250
1251
	// Make sure we fix the language as well.
1252
	if (stristr($language, '-utf8'))
1253
		$changes['language'] = '\'' . str_ireplace('-utf8', '', $language) . '\'';
1254
1255
	// @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...
1256
1257
	// If we are migrating the settings, get them ready.
1258
	if (!empty($_POST['migrateSettings']))
1259
	{
1260
		// Ensure this doesn't get lost in translation.
1261
		$changes['upgradeData'] = '"' . base64_encode(json_encode($upcontext['user'])) . '"';
1262
1263
		migrateSettingsFile($changes);
1264
	}
1265
	else
1266
	{
1267
		// Update Settings.php with the new settings.
1268
		require_once($sourcedir . '/Subs-Admin.php');
1269
		updateSettingsFile($changes);
1270
1271
		// Tell Settings.php to store db_last_error.php in the cache
1272
		move_db_last_error_to_cachedir();
1273
	}
1274
1275
	if ($command_line)
1276
		echo ' Successful.' . "\n";
1277
1278
	// Are we doing debug?
1279
	if (isset($_POST['debug']))
1280
	{
1281
		$upcontext['upgrade_status']['debug'] = true;
1282
		$is_debug = true;
1283
	}
1284
1285
	// If we're not backing up then jump one.
1286
	if (empty($_POST['backup']))
1287
		$upcontext['current_step']++;
1288
1289
	// If we've got here then let's proceed to the next step!
1290
	return true;
1291
}
1292
1293
// Backup the database - why not...
1294
function BackupDatabase()
1295
{
1296
	global $upcontext, $db_prefix, $command_line, $support_js, $file_steps, $smcFunc, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

    public function myFunction() {
        // Do something
    }
}
Loading history...
1297
1298
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1299
	$upcontext['page_title'] = $txt['backup_database'];
1300
1301
	// Done it already - js wise?
1302
	if (!empty($_POST['backup_done']))
1303
		return true;
1304
1305
	// Some useful stuff here.
1306
	db_extend();
1307
1308
	// Might need this as well
1309
	db_extend('packages');
1310
1311
	// Get all the table names.
1312
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1313
	$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...
1314
	$tables = $smcFunc['db_list_tables']($db, $filter);
1315
1316
	$table_names = array();
1317
	foreach ($tables as $table)
1318
		if (substr($table, 0, 7) !== 'backup_')
1319
			$table_names[] = $table;
1320
1321
	$upcontext['table_count'] = count($table_names);
1322
	$upcontext['cur_table_num'] = $_GET['substep'];
1323
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1324
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1325
	// For non-java auto submit...
1326
	$file_steps = $upcontext['table_count'];
1327
1328
	// What ones have we already done?
1329 View Code Duplication
	foreach ($table_names as $id => $table)
1330
		if ($id < $_GET['substep'])
1331
			$upcontext['previous_tables'][] = $table;
1332
1333
	if ($command_line)
1334
		echo 'Backing Up Tables.';
1335
1336
	// If we don't support javascript we backup here.
1337
	if (!$support_js || isset($_GET['xml']))
1338
	{
1339
		// Backup each table!
1340
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1341
		{
1342
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1343
			$upcontext['cur_table_num'] = $substep + 1;
1344
1345
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1346
1347
			// Do we need to pause?
1348
			nextSubstep($substep);
1349
1350
			backupTable($table_names[$substep]);
1351
1352
			// If this is XML to keep it nice for the user do one table at a time anyway!
1353
			if (isset($_GET['xml']))
1354
				return upgradeExit();
1355
		}
1356
1357
		if ($command_line)
1358
		{
1359
			echo "\n" . ' Successful.\'' . "\n";
1360
			flush();
1361
		}
1362
		$upcontext['step_progress'] = 100;
1363
1364
		$_GET['substep'] = 0;
1365
		// Make sure we move on!
1366
		return true;
1367
	}
1368
1369
	// Either way next place to post will be database changes!
1370
	$_GET['substep'] = 0;
1371
	return false;
1372
}
1373
1374
// Backup one table...
1375
function backupTable($table)
1376
{
1377
	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...
1378
1379
	if ($command_line)
1380
	{
1381
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1382
		flush();
1383
	}
1384
1385
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1386
1387
	if ($command_line)
1388
		echo ' done.';
1389
}
1390
1391
// Step 2: Everything.
1392
function DatabaseChanges()
1393
{
1394
	global $db_prefix, $modSettings, $smcFunc, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

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

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

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

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

    public function myFunction() {
        // Do something
    }
}
Loading history...
1490
1491
	// Now it's nice to have some of the basic SMF source files.
1492
	if (!isset($_GET['ssi']) && !$command_line)
1493
		redirectLocation('&ssi=1');
1494
1495
	$upcontext['sub_template'] = 'upgrade_complete';
1496
	$upcontext['page_title'] = $txt['upgrade_complete'];
1497
1498
	$endl = $command_line ? "\n" : '<br>' . "\n";
1499
1500
	$changes = array(
1501
		'language' => '\'' . (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language) . '\'',
1502
		'db_error_send' => '1',
1503
		'upgradeData' => '\'\'',
1504
	);
1505
1506
	// Are we in maintenance mode?
1507
	if (isset($upcontext['user']['main']))
1508
	{
1509
		if ($command_line)
1510
			echo ' * ';
1511
		$upcontext['removed_maintenance'] = true;
1512
		$changes['maintenance'] = $upcontext['user']['main'];
1513
	}
1514
	// Otherwise if somehow we are in 2 let's go to 1.
1515
	elseif (!empty($maintenance) && $maintenance == 2)
1516
		$changes['maintenance'] = 1;
1517
1518
	// Wipe this out...
1519
	$upcontext['user'] = array();
1520
1521
	require_once($sourcedir . '/Subs-Admin.php');
1522
	updateSettingsFile($changes);
1523
1524
	// Clean any old cache files away.
1525
	upgrade_clean_cache();
1526
1527
	// Can we delete the file?
1528
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1529
1530
	// Now is the perfect time to fetch the SM files.
1531
	if ($command_line)
1532
		cli_scheduled_fetchSMfiles();
1533
	else
1534
	{
1535
		require_once($sourcedir . '/ScheduledTasks.php');
1536
		$forum_version = SMF_VERSION; // The variable is usually defined in index.php so lets just use the constant to do it for us.
1537
		scheduled_fetchSMfiles(); // Now go get those files!
1538
		// This is needed in case someone invokes the upgrader using https when upgrading an http forum
1539 View Code Duplication
		if (httpsOn())
1540
			$settings['default_theme_url'] = strtr($settings['default_theme_url'], array('http://' => 'https://'));
1541
	}
1542
1543
	// Log what we've done.
1544
	if (empty($user_info['id']))
1545
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
1546
1547
	// Log the action manually, so CLI still works.
1548
	$smcFunc['db_insert']('',
1549
		'{db_prefix}log_actions',
1550
		array(
1551
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'inet', 'action' => 'string',
1552
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
1553
		),
1554
		array(
1555
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
1556
			0, 0, 0, json_encode(array('version' => $forum_version, 'member' => $user_info['id'])),
1557
		),
1558
		array('id_action')
1559
	);
1560
	$user_info['id'] = 0;
1561
1562
	// Save the current database version.
1563
	$server_version = $smcFunc['db_server_info']();
1564 View Code Duplication
	if ($db_type == 'mysql' && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51')))
1565
		updateSettings(array('db_mysql_group_by_fix' => '1'));
1566
1567
	if ($command_line)
1568
	{
1569
		echo $endl;
1570
		echo 'Upgrade Complete!', $endl;
1571
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
1572
		exit;
1573
	}
1574
1575
	// Make sure it says we're done.
1576
	$upcontext['overall_percent'] = 100;
1577
	if (isset($upcontext['step_progress']))
1578
		unset($upcontext['step_progress']);
1579
1580
	$_GET['substep'] = 0;
1581
	return false;
1582
}
1583
1584
// Just like the built in one, but setup for CLI to not use themes.
1585
function cli_scheduled_fetchSMfiles()
1586
{
1587
	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...
1588
1589
	if (empty($modSettings['time_format']))
1590
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1591
1592
	// What files do we want to get
1593
	$request = $smcFunc['db_query']('', '
1594
		SELECT id_file, filename, path, parameters
1595
		FROM {db_prefix}admin_info_files',
1596
		array(
1597
		)
1598
	);
1599
1600
	$js_files = array();
1601 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
1602
	{
1603
		$js_files[$row['id_file']] = array(
1604
			'filename' => $row['filename'],
1605
			'path' => $row['path'],
1606
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode($forum_version)),
1607
		);
1608
	}
1609
	$smcFunc['db_free_result']($request);
1610
1611
	// We're gonna need fetch_web_data() to pull this off.
1612
	require_once($sourcedir . '/Subs.php');
1613
1614
	foreach ($js_files as $ID_FILE => $file)
1615
	{
1616
		// Create the url
1617
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'https://www.simplemachines.org' : '';
1618
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1619
1620
		// Get the file
1621
		$file_data = fetch_web_data($url);
1622
1623
		// If we got an error - give up - the site might be down.
1624
		if ($file_data === false)
1625
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
1626
1627
		// Save the file to the database.
1628
		$smcFunc['db_query']('substring', '
1629
			UPDATE {db_prefix}admin_info_files
1630
			SET data = SUBSTRING({string:file_data}, 1, 65534)
1631
			WHERE id_file = {int:id_file}',
1632
			array(
1633
				'id_file' => $ID_FILE,
1634
				'file_data' => $file_data,
1635
			)
1636
		);
1637
	}
1638
	return true;
1639
}
1640
1641
function convertSettingsToTheme()
1642
{
1643
	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...
1644
1645
	$values = array(
1646
		'show_latest_member' => @$GLOBALS['showlatestmember'],
1647
		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
1648
		'show_modify' => @$GLOBALS['showmodify'],
1649
		'show_user_images' => @$GLOBALS['showuserpic'],
1650
		'show_blurb' => @$GLOBALS['showusertext'],
1651
		'show_gender' => @$GLOBALS['showgenderimage'],
1652
		'show_newsfader' => @$GLOBALS['shownewsfader'],
1653
		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
1654
		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
1655
		'linktree_link' => @$GLOBALS['curposlinks'],
1656
		'show_profile_buttons' => @$GLOBALS['profilebutton'],
1657
		'show_mark_read' => @$GLOBALS['showmarkread'],
1658
		'newsfader_time' => @$GLOBALS['fadertime'],
1659
		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
1660
		'enable_news' => @$GLOBALS['enable_news'],
1661
		'return_to_post' => @$modSettings['returnToPost'],
1662
	);
1663
1664
	$themeData = array();
1665
	foreach ($values as $variable => $value)
1666
	{
1667
		if (!isset($value) || $value === null)
1668
			$value = 0;
1669
1670
		$themeData[] = array(0, 1, $variable, $value);
1671
	}
1672 View Code Duplication
	if (!empty($themeData))
1673
	{
1674
		$smcFunc['db_insert']('ignore',
1675
			$db_prefix . 'themes',
1676
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
1677
			$themeData,
1678
			array('id_member', 'id_theme', 'variable')
1679
		);
1680
	}
1681
}
1682
1683
// This function only works with MySQL but that's fine as it is only used for v1.0.
1684
function convertSettingstoOptions()
1685
{
1686
	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...
1687
1688
	// Format: new_setting -> old_setting_name.
1689
	$values = array(
1690
		'calendar_start_day' => 'cal_startmonday',
1691
		'view_newest_first' => 'viewNewestFirst',
1692
		'view_newest_pm_first' => 'viewNewestFirst',
1693
	);
1694
1695
	foreach ($values as $variable => $value)
1696
	{
1697
		if (empty($modSettings[$value[0]]))
1698
			continue;
1699
1700
		$smcFunc['db_query']('', '
1701
			INSERT IGNORE INTO {db_prefix}themes
1702
				(id_member, id_theme, variable, value)
1703
			SELECT id_member, 1, {string:variable}, {string:value}
1704
			FROM {db_prefix}members',
1705
			array(
1706
				'variable' => $variable,
1707
				'value' => $modSettings[$value[0]],
1708
				'db_error_skip' => true,
1709
			)
1710
		);
1711
1712
		$smcFunc['db_query']('', '
1713
			INSERT IGNORE INTO {db_prefix}themes
1714
				(id_member, id_theme, variable, value)
1715
			VALUES (-1, 1, {string:variable}, {string:value})',
1716
			array(
1717
				'variable' => $variable,
1718
				'value' => $modSettings[$value[0]],
1719
				'db_error_skip' => true,
1720
			)
1721
		);
1722
	}
1723
}
1724
1725
function php_version_check()
1726
{
1727
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
1728
}
1729
1730
function db_version_check()
1731
{
1732
	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...
1733
1734
	$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...
1735
	$curver = preg_replace('~\-.+?$~', '', $curver);
1736
1737
	return version_compare($databases[$db_type]['version'], $curver, '<=');
1738
}
1739
1740
function fixRelativePath($path)
1741
{
1742
	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...
1743
1744
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
1745
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
1746
}
1747
1748
function parse_sql($filename)
1749
{
1750
	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...
1751
	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...
1752
1753
/*
1754
	Failure allowed on:
1755
		- INSERT INTO but not INSERT IGNORE INTO.
1756
		- UPDATE IGNORE but not UPDATE.
1757
		- ALTER TABLE and ALTER IGNORE TABLE.
1758
		- DROP TABLE.
1759
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
1760
1761
	If a comment...
1762
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
1763
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
1764
		- is only ---#, it is "done." and then a break - only shown in debug.
1765
		- begins with ---{ it is a code block terminating at ---}.
1766
1767
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
1768
1769
	Replaces the following variables:
1770
		- {$boarddir}
1771
		- {$boardurl}
1772
		- {$db_prefix}
1773
		- {$db_collation}
1774
*/
1775
1776
	// May want to use extended functionality.
1777
	db_extend();
1778
	db_extend('packages');
1779
1780
	// Our custom error handler - does nothing but does stop public errors from XML!
1781
	set_error_handler(
1782
		function ($errno, $errstr, $errfile, $errline) use ($support_js)
1783
		{
1784
			if ($support_js)
1785
				return true;
1786
			else
1787
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
1788
		}
1789
	);
1790
1791
	// If we're on MySQL, set {db_collation}; this approach is used throughout upgrade_2-0_mysql.php to set new tables to utf8
1792
	// Note it is expected to be in the format: ENGINE=MyISAM{$db_collation};
1793
	if ($db_type == 'mysql')
1794
		$db_collation = ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1795
	else
1796
		$db_collation = '';
1797
1798
	$endl = $command_line ? "\n" : '<br>' . "\n";
1799
1800
	$lines = file($filename);
1801
1802
	$current_type = 'sql';
1803
	$current_data = '';
1804
	$substep = 0;
1805
	$last_step = '';
1806
1807
	// Make sure all newly created tables will have the proper characters set; this approach is used throughout upgrade_2-1_mysql.php
1808
	if (isset($db_character_set) && $db_character_set === 'utf8')
1809
		$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
1810
1811
	// Count the total number of steps within this file - for progress.
1812
	$file_steps = substr_count(implode('', $lines), '---#');
1813
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
1814
	$upcontext['debug_items'] = $file_steps;
1815
	$upcontext['current_item_num'] = 0;
1816
	$upcontext['current_item_name'] = '';
1817
	$upcontext['current_debug_item_num'] = 0;
1818
	$upcontext['current_debug_item_name'] = '';
1819
	// This array keeps a record of what we've done in case java is dead...
1820
	$upcontext['actioned_items'] = array();
1821
1822
	$done_something = false;
1823
1824
	foreach ($lines as $line_number => $line)
1825
	{
1826
		$do_current = $substep >= $_GET['substep'];
1827
1828
		// Get rid of any comments in the beginning of the line...
1829
		if (substr(trim($line), 0, 2) === '/*')
1830
			$line = preg_replace('~/\*.+?\*/~', '', $line);
1831
1832
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
1833
		if ($is_debug && !$support_js && $command_line)
1834
			flush();
1835
1836
		if (trim($line) === '')
1837
			continue;
1838
1839
		if (trim(substr($line, 0, 3)) === '---')
1840
		{
1841
			$type = substr($line, 3, 1);
1842
1843
			// An error??
1844
			if (trim($current_data) != '' && $type !== '}')
1845
			{
1846
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
1847
				if ($command_line)
1848
					echo $upcontext['error_message'];
1849
			}
1850
1851
			if ($type == ' ')
1852
			{
1853
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
1854
				{
1855
					echo ' Successful.', $endl;
1856
					flush();
1857
				}
1858
1859
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
1860
				$upcontext['current_item_num']++;
1861
				$upcontext['current_item_name'] = $last_step;
1862
1863
				if ($do_current)
1864
				{
1865
					$upcontext['actioned_items'][] = $last_step;
1866
					if ($command_line)
1867
						echo ' * ';
1868
				}
1869
			}
1870
			elseif ($type == '#')
1871
			{
1872
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
1873
1874
				$upcontext['current_debug_item_num']++;
1875
				if (trim($line) != '---#')
1876
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
1877
1878
				// Have we already done something?
1879
				if (isset($_GET['xml']) && $done_something)
1880
				{
1881
					restore_error_handler();
1882
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
1883
				}
1884
1885
				if ($do_current)
1886
				{
1887
					if (trim($line) == '---#' && $command_line)
1888
						echo ' done.', $endl;
1889
					elseif ($command_line)
1890
						echo ' +++ ', rtrim(substr($line, 4));
1891
					elseif (trim($line) != '---#')
1892
					{
1893
						if ($is_debug)
1894
							$upcontext['actioned_items'][] = htmlspecialchars(rtrim(substr($line, 4)));
1895
					}
1896
				}
1897
1898
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
1899
				{
1900
					if ($command_line)
1901
						echo ' * ';
1902
					else
1903
						$upcontext['actioned_items'][] = $last_step;
1904
				}
1905
1906
				// Small step - only if we're actually doing stuff.
1907
				if ($do_current)
1908
					nextSubstep(++$substep);
1909
				else
1910
					$substep++;
1911
			}
1912
			elseif ($type == '{')
1913
				$current_type = 'code';
1914
			elseif ($type == '}')
1915
			{
1916
				$current_type = 'sql';
1917
1918
				if (!$do_current)
1919
				{
1920
					$current_data = '';
1921
					continue;
1922
				}
1923
1924
				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...
1925
				{
1926
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
1927
					if ($command_line)
1928
						echo $upcontext['error_message'];
1929
				}
1930
1931
				// Done with code!
1932
				$current_data = '';
1933
				$done_something = true;
1934
			}
1935
1936
			continue;
1937
		}
1938
1939
		$current_data .= $line;
1940
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
1941
		{
1942
			if ((!$support_js || isset($_GET['xml'])))
1943
			{
1944
				if (!$do_current)
1945
				{
1946
					$current_data = '';
1947
					continue;
1948
				}
1949
1950
				$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));
1951
1952
				upgrade_query($current_data);
1953
1954
				// @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...
1955
				/*
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...
1956
				$result = $smcFunc['db_query']('', $current_data, false, false);
1957
				// Went wrong?
1958
				if (!$result)
1959
				{
1960
					// Bit of a bodge - do we want the error?
1961
					if (!empty($upcontext['return_error']))
1962
					{
1963
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
1964
						return false;
1965
					}
1966
				}*/
1967
				$done_something = true;
1968
			}
1969
			$current_data = '';
1970
		}
1971
		// If this is xml based and we're just getting the item name then that's grand.
1972
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
1973
		{
1974
			restore_error_handler();
1975
			return false;
1976
		}
1977
1978
		// Clean up by cleaning any step info.
1979
		$step_progress = array();
1980
		$custom_warning = '';
1981
	}
1982
1983
	// Put back the error handler.
1984
	restore_error_handler();
1985
1986
	if ($command_line)
1987
	{
1988
		echo ' Successful.' . "\n";
1989
		flush();
1990
	}
1991
1992
	$_GET['substep'] = 0;
1993
	return true;
1994
}
1995
1996
function upgrade_query($string, $unbuffered = false)
1997
{
1998
	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...
1999
	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...
2000
2001
	// Get the query result - working around some SMF specific security - just this once!
2002
	$modSettings['disableQueryCheck'] = true;
2003
	$db_unbuffered = $unbuffered;
2004
	$ignore_insert_error = false;
2005
2006
	// If we got an old pg version and use a insert ignore query
2007
	if ($db_type == 'postgresql' && !$smcFunc['db_native_replace']() && strpos($string, 'ON CONFLICT DO NOTHING') !== false)
2008
	{
2009
		$ignore_insert_error = true;
2010
		$string = str_replace('ON CONFLICT DO NOTHING', '', $string);
2011
	}
2012
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
2013
	$db_unbuffered = false;
2014
2015
	// Failure?!
2016
	if ($result !== false)
2017
		return $result;
2018
2019
	$db_error_message = $smcFunc['db_error']($db_connection);
2020
	// If MySQL we do something more clever.
2021
	if ($db_type == 'mysql')
2022
	{
2023
		$mysqli_errno = mysqli_errno($db_connection);
2024
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR'));
2025
2026
		// Error numbers:
2027
		//    1016: Can't open file '....MYI'
2028
		//    1050: Table already exists.
2029
		//    1054: Unknown column name.
2030
		//    1060: Duplicate column name.
2031
		//    1061: Duplicate key name.
2032
		//    1062: Duplicate entry for unique key.
2033
		//    1068: Multiple primary keys.
2034
		//    1072: Key column '%s' doesn't exist in table.
2035
		//    1091: Can't drop key, doesn't exist.
2036
		//    1146: Table doesn't exist.
2037
		//    2013: Lost connection to server during query.
2038
2039
		if ($mysqli_errno == 1016)
2040
		{
2041
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
2042
			{
2043
				mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
2044
				$result = mysqli_query($db_connection, $string);
2045
				if ($result !== false)
2046
					return $result;
2047
			}
2048
		}
2049
		elseif ($mysqli_errno == 2013)
2050
		{
2051
			$db_connection = mysqli_connect($db_server, $db_user, $db_passwd);
2052
			mysqli_select_db($db_connection, $db_name);
2053
			if ($db_connection)
2054
			{
2055
				$result = mysqli_query($db_connection, $string);
2056
				if ($result !== false)
2057
					return $result;
2058
			}
2059
		}
2060
		// Duplicate column name... should be okay ;).
2061 View Code Duplication
		elseif (in_array($mysqli_errno, array(1060, 1061, 1068, 1091)))
2062
			return false;
2063
		// Duplicate insert... make sure it's the proper type of query ;).
2064 View Code Duplication
		elseif (in_array($mysqli_errno, array(1054, 1062, 1146)) && $error_query)
2065
			return false;
2066
		// Creating an index on a non-existent column.
2067
		elseif ($mysqli_errno == 1072)
2068
			return false;
2069
		elseif ($mysqli_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
2070
			return false;
2071
	}
2072
	// If a table already exists don't go potty.
2073
	else
2074
	{
2075
		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U')))
2076
		{
2077
			if (strpos($db_error_message, 'exist') !== false)
2078
				return true;
2079
		}
2080
		elseif (strpos(trim($string), 'INSERT ') !== false)
2081
		{
2082
			if (strpos($db_error_message, 'duplicate') !== false || $ignore_insert_error)
2083
				return true;
2084
		}
2085
	}
2086
2087
	// Get the query string so we pass everything.
2088
	$query_string = '';
2089
	foreach ($_GET as $k => $v)
2090
		$query_string .= ';' . $k . '=' . $v;
2091
	if (strlen($query_string) != 0)
2092
		$query_string = '?' . substr($query_string, 1);
2093
2094
	if ($command_line)
2095
	{
2096
		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
2097
		die;
2098
	}
2099
2100
	// Bit of a bodge - do we want the error?
2101
	if (!empty($upcontext['return_error']))
2102
	{
2103
		$upcontext['error_message'] = $db_error_message;
2104
		$upcontext['error_string'] = $string;
2105
		return false;
2106
	}
2107
2108
	// Otherwise we have to display this somewhere appropriate if possible.
2109
	$upcontext['forced_error_message'] = '
2110
			<strong>Unsuccessful!</strong><br>
2111
2112
			<div style="margin: 2ex;">
2113
				This query:
2114
				<blockquote><pre>' . nl2br(htmlspecialchars(trim($string))) . ';</pre></blockquote>
2115
2116
				Caused the error:
2117
				<blockquote>' . nl2br(htmlspecialchars($db_error_message)) . '</blockquote>
2118
			</div>
2119
2120
			<form action="' . $upgradeurl . $query_string . '" method="post">
2121
				<input type="submit" value="Try again" class="button">
2122
			</form>
2123
		</div>';
2124
2125
	upgradeExit();
2126
}
2127
2128
// This performs a table alter, but does it unbuffered so the script can time out professionally.
2129
function protected_alter($change, $substep, $is_test = false)
2130
{
2131
	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...
2132
2133
	db_extend('packages');
2134
2135
	// Firstly, check whether the current index/column exists.
2136
	$found = false;
2137
	if ($change['type'] === 'column')
2138
	{
2139
		$columns = $smcFunc['db_list_columns']('{db_prefix}' . $change['table'], true);
2140
		foreach ($columns as $column)
2141
		{
2142
			// Found it?
2143
			if ($column['name'] === $change['name'])
2144
			{
2145
				$found |= 1;
2146
				// Do some checks on the data if we have it set.
2147
				if (isset($change['col_type']))
2148
					$found &= $change['col_type'] === $column['type'];
2149
				if (isset($change['null_allowed']))
2150
					$found &= $column['null'] == $change['null_allowed'];
2151
				if (isset($change['default']))
2152
					$found &= $change['default'] === $column['default'];
2153
			}
2154
		}
2155
	}
2156
	elseif ($change['type'] === 'index')
2157
	{
2158
		$request = upgrade_query('
2159
			SHOW INDEX
2160
			FROM ' . $db_prefix . $change['table']);
2161
		if ($request !== false)
2162
		{
2163
			$cur_index = array();
2164
2165
			while ($row = $smcFunc['db_fetch_assoc']($request))
2166
				if ($row['Key_name'] === $change['name'])
2167
					$cur_index[(int) $row['Seq_in_index']] = $row['Column_name'];
2168
2169
			ksort($cur_index, SORT_NUMERIC);
2170
			$found = array_values($cur_index) === $change['target_columns'];
2171
2172
			$smcFunc['db_free_result']($request);
2173
		}
2174
	}
2175
2176
	// If we're trying to add and it's added, we're done.
2177
	if ($found && in_array($change['method'], array('add', 'change')))
2178
		return true;
2179
	// Otherwise if we're removing and it wasn't found we're also done.
2180
	elseif (!$found && in_array($change['method'], array('remove', 'change_remove')))
2181
		return true;
2182
	// Otherwise is it just a test?
2183
	elseif ($is_test)
2184
		return false;
2185
2186
	// Not found it yet? Bummer! How about we see if we're currently doing it?
2187
	$running = false;
2188
	$found = false;
2189
	while (1 == 1)
2190
	{
2191
		$request = upgrade_query('
2192
			SHOW FULL PROCESSLIST');
2193
		while ($row = $smcFunc['db_fetch_assoc']($request))
2194
		{
2195
			if (strpos($row['Info'], 'ALTER TABLE ' . $db_prefix . $change['table']) !== false && strpos($row['Info'], $change['text']) !== false)
2196
				$found = true;
2197
		}
2198
2199
		// Can't find it? Then we need to run it fools!
2200
		if (!$found && !$running)
2201
		{
2202
			$smcFunc['db_free_result']($request);
2203
2204
			$success = upgrade_query('
2205
				ALTER TABLE ' . $db_prefix . $change['table'] . '
2206
				' . $change['text'], true) !== false;
2207
2208
			if (!$success)
2209
				return false;
2210
2211
			// Return
2212
			$running = true;
2213
		}
2214
		// What if we've not found it, but we'd ran it already? Must of completed.
2215
		elseif (!$found)
2216
		{
2217
			$smcFunc['db_free_result']($request);
2218
			return true;
2219
		}
2220
2221
		// Pause execution for a sec or three.
2222
		sleep(3);
2223
2224
		// Can never be too well protected.
2225
		nextSubstep($substep);
2226
	}
2227
2228
	// Protect it.
2229
	nextSubstep($substep);
2230
}
2231
2232
/**
2233
 * Alter a text column definition preserving its character set.
2234
 *
2235
 * @param array $change
2236
 * @param int $substep
2237
 */
2238
function textfield_alter($change, $substep)
2239
{
2240
	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...
2241
2242
	$request = $smcFunc['db_query']('', '
2243
		SHOW FULL COLUMNS
2244
		FROM {db_prefix}' . $change['table'] . '
2245
		LIKE {string:column}',
2246
		array(
2247
			'column' => $change['column'],
2248
			'db_error_skip' => true,
2249
		)
2250
	);
2251
	if ($smcFunc['db_num_rows']($request) === 0)
2252
		die('Unable to find column ' . $change['column'] . ' inside table ' . $db_prefix . $change['table']);
2253
	$table_row = $smcFunc['db_fetch_assoc']($request);
2254
	$smcFunc['db_free_result']($request);
2255
2256
	// If something of the current column definition is different, fix it.
2257
	$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']);
2258
2259
	// Columns that previously allowed null, need to be converted first.
2260
	$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
2261
2262
	// Get the character set that goes with the collation of the column.
2263 View Code Duplication
	if ($column_fix && !empty($table_row['Collation']))
2264
	{
2265
		$request = $smcFunc['db_query']('', '
2266
			SHOW COLLATION
2267
			LIKE {string:collation}',
2268
			array(
2269
				'collation' => $table_row['Collation'],
2270
				'db_error_skip' => true,
2271
			)
2272
		);
2273
		// No results? Just forget it all together.
2274
		if ($smcFunc['db_num_rows']($request) === 0)
2275
			unset($table_row['Collation']);
2276
		else
2277
			$collation_info = $smcFunc['db_fetch_assoc']($request);
2278
		$smcFunc['db_free_result']($request);
2279
	}
2280
2281
	if ($column_fix)
2282
	{
2283
		// Make sure there are no NULL's left.
2284
		if ($null_fix)
2285
			$smcFunc['db_query']('', '
2286
				UPDATE {db_prefix}' . $change['table'] . '
2287
				SET ' . $change['column'] . ' = {string:default}
2288
				WHERE ' . $change['column'] . ' IS NULL',
2289
				array(
2290
					'default' => isset($change['default']) ? $change['default'] : '',
2291
					'db_error_skip' => true,
2292
				)
2293
			);
2294
2295
		// Do the actual alteration.
2296
		$smcFunc['db_query']('', '
2297
			ALTER TABLE {db_prefix}' . $change['table'] . '
2298
			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}' : ''),
2299
			array(
2300
				'default' => isset($change['default']) ? $change['default'] : '',
2301
				'db_error_skip' => true,
2302
			)
2303
		);
2304
	}
2305
	nextSubstep($substep);
2306
}
2307
2308
// Check if we need to alter this query.
2309
function checkChange(&$change)
2310
{
2311
	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...
2312
	static $database_version, $where_field_support;
2313
2314
	// Attempt to find a database_version.
2315
	if (empty($database_version))
2316
	{
2317
		$database_version = $databases[$db_type]['version_check'];
2318
		$where_field_support = $db_type == 'mysql' && version_compare('5.0', $database_version, '<=');
2319
	}
2320
2321
	// Not a column we need to check on?
2322
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
2323
		return;
2324
2325
	// Break it up you (six|seven).
2326
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
2327
2328
	// Can we support a shortcut method?
2329
	if ($where_field_support)
2330
	{
2331
		// Get the details about this change.
2332
		$request = $smcFunc['db_query']('', '
2333
			SHOW FIELDS
2334
			FROM {db_prefix}{raw:table}
2335
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
2336
			array(
2337
				'table' => $change['table'],
2338
				'old_name' => $temp[1],
2339
				'new_name' => $temp[2],
2340
		));
2341
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2342
		if ($smcFunc['db_num_rows'] != 1)
2343
			return;
2344
2345
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
2346
		$smcFunc['db_free_result']($request);
2347
	}
2348
	else
2349
	{
2350
		// Do this the old fashion, sure method way.
2351
		$request = $smcFunc['db_query']('', '
2352
			SHOW FIELDS
2353
			FROM {db_prefix}{raw:table}',
2354
			array(
2355
				'table' => $change['table'],
2356
		));
2357
		// Mayday!
2358
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2359
		if ($smcFunc['db_num_rows'] == 0)
2360
			return;
2361
2362
		// Oh where, oh where has my little field gone. Oh where can it be...
2363
		while ($row = $smcFunc['db_query']($request))
2364
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
2365
			{
2366
				$current_type = $row['Type'];
2367
				break;
2368
			}
2369
	}
2370
2371
	// If this doesn't match, the column may of been altered for a reason.
2372
	if (trim($current_type) != trim($temp[3]))
2373
		$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...
2374
2375
	// Piece this back together.
2376
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
2377
}
2378
2379
// The next substep.
2380
function nextSubstep($substep)
2381
{
2382
	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...
2383
	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...
2384
2385
	if ($_GET['substep'] < $substep)
2386
		$_GET['substep'] = $substep;
2387
2388
	if ($command_line)
2389
	{
2390
		if (time() - $start_time > 1 && empty($is_debug))
2391
		{
2392
			echo '.';
2393
			$start_time = time();
2394
		}
2395
		return;
2396
	}
2397
2398
	@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...
2399
	if (function_exists('apache_reset_timeout'))
2400
		@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...
2401
2402
	if (time() - $start_time <= $timeLimitThreshold)
2403
		return;
2404
2405
	// Do we have some custom step progress stuff?
2406
	if (!empty($step_progress))
2407
	{
2408
		$upcontext['substep_progress'] = 0;
2409
		$upcontext['substep_progress_name'] = $step_progress['name'];
2410
		if ($step_progress['current'] > $step_progress['total'])
2411
			$upcontext['substep_progress'] = 99.9;
2412
		else
2413
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2414
2415
		// Make it nicely rounded.
2416
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2417
	}
2418
2419
	// If this is XML we just exit right away!
2420
	if (isset($_GET['xml']))
2421
		return upgradeExit();
2422
2423
	// We're going to pause after this!
2424
	$upcontext['pause'] = true;
2425
2426
	$upcontext['query_string'] = '';
2427
	foreach ($_GET as $k => $v)
2428
	{
2429
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2430
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2431
	}
2432
2433
	// Custom warning?
2434
	if (!empty($custom_warning))
2435
		$upcontext['custom_warning'] = $custom_warning;
2436
2437
	upgradeExit();
2438
}
2439
2440
function cmdStep0()
2441
{
2442
	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...
2443
	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...
2444
	$start_time = time();
2445
2446
	ob_end_clean();
2447
	ob_implicit_flush(true);
2448
	@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...
2449
2450
	if (!isset($_SERVER['argv']))
2451
		$_SERVER['argv'] = array();
2452
	$_GET['maint'] = 1;
2453
2454
	foreach ($_SERVER['argv'] as $i => $arg)
2455
	{
2456
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2457
			$_GET['lang'] = $match[1];
2458
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2459
			continue;
2460
		elseif ($arg == '--no-maintenance')
2461
			$_GET['maint'] = 0;
2462
		elseif ($arg == '--debug')
2463
			$is_debug = true;
2464
		elseif ($arg == '--backup')
2465
			$_POST['backup'] = 1;
2466
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2467
			$_GET['conv'] = 1;
2468
		elseif ($i != 0)
2469
		{
2470
			echo 'SMF Command-line Upgrader
2471
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2472
2473
    --language=LANG         Reset the forum\'s language to LANG.
2474
    --no-maintenance        Don\'t put the forum into maintenance mode.
2475
    --debug                 Output debugging information.
2476
    --backup                Create backups of tables with "backup_" prefix.';
2477
			echo "\n";
2478
			exit;
2479
		}
2480
	}
2481
2482
	if (!php_version_check())
2483
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
2484
	if (!db_version_check())
2485
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2486
2487
	// Do some checks to make sure they have proper privileges
2488
	db_extend('packages');
2489
2490
	// CREATE
2491
	$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');
2492
2493
	// ALTER
2494
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
2495
2496
	// DROP
2497
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2498
2499
	// Sorry... we need CREATE, ALTER and DROP
2500 View Code Duplication
	if (!$create || !$alter || !$drop)
2501
		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...
2502
2503
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2504
		&& @file_exists($sourcedir . '/QueryString.php')
2505
		&& @file_exists($sourcedir . '/ManageBoards.php');
2506
	if (!$check && !isset($modSettings['smfVersion']))
2507
		print_error('Error: Some files are missing or out-of-date.', true);
2508
2509
	// Do a quick version spot check.
2510
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
2511
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2512
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2513
		print_error('Error: Some files have not yet been updated properly.');
2514
2515
	// Make sure Settings.php is writable.
2516
	quickFileWritable($boarddir . '/Settings.php');
2517
	if (!is_writable($boarddir . '/Settings.php'))
2518
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2519
2520
	// Make sure Settings_bak.php is writable.
2521
	quickFileWritable($boarddir . '/Settings_bak.php');
2522
	if (!is_writable($boarddir . '/Settings_bak.php'))
2523
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2524
2525 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
2526
		print_error('Error: Unable to obtain write access to "agreement.txt".');
2527
	elseif (isset($modSettings['agreement']))
2528
	{
2529
		$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...
2530
		fwrite($fp, $modSettings['agreement']);
2531
		fclose($fp);
2532
	}
2533
2534
	// Make sure Themes is writable.
2535
	quickFileWritable($modSettings['theme_dir']);
2536
2537
	if (!is_writable($modSettings['theme_dir']) && !isset($modSettings['smfVersion']))
2538
		print_error('Error: Unable to obtain write access to "Themes".');
2539
2540
	// Make sure cache directory exists and is writable!
2541
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
2542
	if (!file_exists($cachedir_temp))
2543
		@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...
2544
2545
	// Make sure the cache temp dir is writable.
2546
	quickFileWritable($cachedir_temp);
2547
2548
	if (!is_writable($cachedir_temp))
2549
		print_error('Error: Unable to obtain write access to "cache".', true);
2550
2551
	// Make sure db_last_error.php is writable.
2552
	quickFileWritable($cachedir_temp . '/db_last_error.php');
2553
	if (!is_writable($cachedir_temp . '/db_last_error.php'))
2554
		print_error('Error: Unable to obtain write access to "db_last_error.php".');
2555
2556
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
2557
		print_error('Error: Unable to find language files!', true);
2558
	else
2559
	{
2560
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
2561
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
2562
2563
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
2564
			print_error('Error: Language files out of date.', true);
2565
		if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
2566
			print_error('Error: Install language is missing for selected language.', true);
2567
2568
		// Otherwise include it!
2569
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
2570
	}
2571
2572
	// Make sure we skip the HTML for login.
2573
	$_POST['upcont'] = true;
2574
	$upcontext['current_step'] = 1;
2575
}
2576
2577
/**
2578
 * Handles converting your database to UTF-8
2579
 */
2580
function ConvertUtf8()
2581
{
2582
	global $upcontext, $db_character_set, $sourcedir, $smcFunc, $modSettings, $language;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

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

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

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

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

    public function myFunction() {
        // Do something
    }
}
Loading history...
3093
3094
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'serialize_json_xml' : 'serialize_json';
3095
	// First thing's first - did we already do this?
3096
	if (!empty($modSettings['json_done']))
3097
	{
3098
		if ($command_line)
3099
			return DeleteUpgrade();
3100
		else
3101
			return true;
3102
	}
3103
3104
	// Done it already - js wise?
3105
	if (!empty($_POST['json_done']))
3106
		return true;
3107
3108
	// List of tables affected by this function
3109
	// 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...
3110
	// If 3rd item in array is true, it indicates that col1 could be empty...
3111
	$tables = array(
3112
		'background_tasks' => array('id_task', 'task_data'),
3113
		'log_actions' => array('id_action', 'extra'),
3114
		'log_online' => array('session', 'url'),
3115
		'log_packages' => array('id_install', 'db_changes', 'failed_steps', 'credits'),
3116
		'log_spider_hits' => array('id_hit', 'url'),
3117
		'log_subscribed' => array('id_sublog', 'pending_details'),
3118
		'pm_rules' => array('id_rule', 'criteria', 'actions'),
3119
		'qanda' => array('id_question', 'answers'),
3120
		'subscriptions' => array('id_subscribe', 'cost'),
3121
		'user_alerts' => array('id_alert', 'extra', true),
3122
		'user_drafts' => array('id_draft', 'to_list', true),
3123
		// These last two are a bit different - we'll handle those separately
3124
		'settings' => array(),
3125
		'themes' => array()
3126
	);
3127
3128
	// Set up some context stuff...
3129
	// Because we're not using numeric indices, we need this to figure out the current table name...
3130
	$keys = array_keys($tables);
3131
3132
	$upcontext['page_title'] = $txt['converting_json'];
3133
	$upcontext['table_count'] = count($keys);
3134
	$upcontext['cur_table_num'] = $_GET['substep'];
3135
	$upcontext['cur_table_name'] = isset($keys[$_GET['substep']]) ? $keys[$_GET['substep']] : $keys[0];
3136
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3137
3138 View Code Duplication
	foreach ($keys as $id => $table)
3139
		if ($id < $_GET['substep'])
3140
			$upcontext['previous_tables'][] = $table;
3141
3142
	if ($command_line)
3143
		echo 'Converting data from serialize() to json_encode().';
3144
3145
	if (!$support_js || isset($_GET['xml']))
3146
	{
3147
		// Fix the data in each table
3148
		for ($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
3149
		{
3150
			$upcontext['cur_table_name'] = isset($keys[$substep + 1]) ? $keys[$substep + 1] : $keys[$substep];
3151
			$upcontext['cur_table_num'] = $substep + 1;
3152
3153
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3154
3155
			// Do we need to pause?
3156
			nextSubstep($substep);
3157
3158
			// Initialize a few things...
3159
			$where = '';
3160
			$vars = array();
3161
			$table = $keys[$substep];
3162
			$info = $tables[$table];
3163
3164
			// Now the fun - build our queries and all that fun stuff
3165
			if ($table == 'settings')
3166
			{
3167
				// Now a few settings...
3168
				$serialized_settings = array(
3169
					'attachment_basedirectories',
3170
					'attachmentUploadDir',
3171
					'cal_today_birthday',
3172
					'cal_today_event',
3173
					'cal_today_holiday',
3174
					'displayFields',
3175
					'last_attachments_directory',
3176
					'memberlist_cache',
3177
					'search_custom_index_config',
3178
					'spider_name_cache'
3179
				);
3180
3181
				// Loop through and fix these...
3182
				$new_settings = array();
3183
				if ($command_line)
3184
					echo "\n" . 'Fixing some settings...';
3185
3186
				foreach ($serialized_settings as $var)
3187
				{
3188
					if (isset($modSettings[$var]))
3189
					{
3190
						// Attempt to unserialize the setting
3191
						$temp = @safe_unserialize($modSettings[$var]);
3192
						if (!$temp && $command_line)
3193
							echo "\n - Failed to unserialize the '" . $var . "' setting. Skipping.";
3194
						elseif ($temp !== false)
3195
							$new_settings[$var] = json_encode($temp);
3196
					}
3197
				}
3198
3199
				// Update everything at once
3200
				if (!function_exists('cache_put_data'))
3201
					require_once($sourcedir . '/Load.php');
3202
				updateSettings($new_settings, true);
3203
3204
				if ($command_line)
3205
					echo ' done.';
3206
			}
3207
			elseif ($table == 'themes')
3208
			{
3209
				// Finally, fix the admin prefs. Unfortunately this is stored per theme, but hopefully they only have one theme installed at this point...
3210
				$query = $smcFunc['db_query']('', '
3211
					SELECT id_member, id_theme, value FROM {db_prefix}themes
3212
					WHERE variable = {string:admin_prefs}',
3213
						array(
3214
							'admin_prefs' => 'admin_preferences'
3215
						)
3216
				);
3217
3218
				if ($smcFunc['db_num_rows']($query) != 0)
3219
				{
3220
					while ($row = $smcFunc['db_fetch_assoc']($query))
3221
					{
3222
						$temp = @safe_unserialize($row['value']);
3223
3224
						if ($command_line)
3225
						{
3226
							if ($temp === false)
3227
								echo "\n" . 'Unserialize of admin_preferences for user ' . $row['id_member'] . ' failed. Skipping.';
3228
							else
3229
								echo "\n" . 'Fixing admin preferences...';
3230
						}
3231
3232
						if ($temp !== false)
3233
						{
3234
							$row['value'] = json_encode($temp);
3235
3236
							// Even though we have all values from the table, UPDATE is still faster than REPLACE
3237
							$smcFunc['db_query']('', '
3238
								UPDATE {db_prefix}themes
3239
								SET value = {string:prefs}
3240
								WHERE id_theme = {int:theme}
3241
									AND id_member = {int:member}
3242
									AND variable = {string:admin_prefs}',
3243
								array(
3244
									'prefs' => $row['value'],
3245
									'theme' => $row['id_theme'],
3246
									'member' => $row['id_member'],
3247
									'admin_prefs' => 'admin_preferences'
3248
								)
3249
							);
3250
3251
							if ($command_line)
3252
								echo ' done.';
3253
						}
3254
					}
3255
3256
					$smcFunc['db_free_result']($query);
3257
				}
3258
			}
3259
			else
3260
			{
3261
				// First item is always the key...
3262
				$key = $info[0];
3263
				unset($info[0]);
3264
3265
				// Now we know what columns we have and such...
3266
				if (count($info) == 2 && $info[2] === true)
3267
				{
3268
					$col_select = $info[1];
3269
					$where = ' WHERE ' . $info[1] . ' != {empty}';
3270
				}
3271
				else
3272
				{
3273
					$col_select = implode(', ', $info);
3274
				}
3275
3276
				$query = $smcFunc['db_query']('', '
3277
					SELECT ' . $key . ', ' . $col_select . '
3278
					FROM {db_prefix}' . $table . $where,
3279
					array()
3280
				);
3281
3282
				if ($smcFunc['db_num_rows']($query) != 0)
3283
				{
3284
					if ($command_line)
3285
					{
3286
						echo "\n" . ' +++ Fixing the "' . $table . '" table...';
3287
						flush();
3288
					}
3289
3290
					while ($row = $smcFunc['db_fetch_assoc']($query))
3291
					{
3292
						$update = '';
3293
3294
						// We already know what our key is...
3295
						foreach ($info as $col)
3296
						{
3297
							if ($col !== true && $row[$col] != '')
3298
							{
3299
								$temp = @safe_unserialize($row[$col]);
3300
3301
								if ($temp === false && $command_line)
3302
								{
3303
									echo "\nFailed to unserialize " . $row[$col] . "... Skipping\n";
3304
								}
3305
								else
3306
								{
3307
									$row[$col] = json_encode($temp);
3308
3309
									// Build our SET string and variables array
3310
									$update .= (empty($update) ? '' : ', ') . $col . ' = {string:' . $col . '}';
3311
									$vars[$col] = $row[$col];
3312
								}
3313
							}
3314
						}
3315
3316
						$vars[$key] = $row[$key];
3317
3318
						// In a few cases, we might have empty data, so don't try to update in those situations...
3319
						if (!empty($update))
3320
						{
3321
							$smcFunc['db_query']('', '
3322
								UPDATE {db_prefix}' . $table . '
3323
								SET ' . $update . '
3324
								WHERE ' . $key . ' = {' . ($key == 'session' ? 'string' : 'int') . ':' . $key . '}',
3325
								$vars
3326
							);
3327
						}
3328
					}
3329
3330
					if ($command_line)
3331
						echo ' done.';
3332
3333
					// Free up some memory...
3334
					$smcFunc['db_free_result']($query);
3335
				}
3336
			}
3337
			// If this is XML to keep it nice for the user do one table at a time anyway!
3338
			if (isset($_GET['xml']))
3339
				return upgradeExit();
3340
		}
3341
3342
		if ($command_line)
3343
		{
3344
			echo "\n" . 'Successful.' . "\n";
3345
			flush();
3346
		}
3347
		$upcontext['step_progress'] = 100;
3348
3349
		// Last but not least, insert a dummy setting so we don't have to do this again in the future...
3350
		updateSettings(array('json_done' => true));
3351
3352
		$_GET['substep'] = 0;
3353
		// Make sure we move on!
3354
		if ($command_line)
3355
			return DeleteUpgrade();
3356
3357
		return true;
3358
	}
3359
3360
	// If this fails we just move on to deleting the upgrade anyway...
3361
	$_GET['substep'] = 0;
3362
	return false;
3363
}
3364
3365
/**
3366
 * As of 2.1, we want to store db_last_error.php in the cache
3367
 * To make that happen, Settings.php needs to ensure the $cachedir path is correct before trying to write to db_last_error.php
3368
 */
3369
function move_db_last_error_to_cachedir()
3370
{
3371
	$settings = file_get_contents(dirname(__FILE__) . '/Settings.php');
3372
3373
	$regex = <<<'EOT'
3374
(\s*#\s*Make\s+sure\s+the\s+paths\s+are\s+correct\.\.\.\s+at\s+least\s+try\s+to\s+fix\s+them\.\s+)?if\s*\(\!file_exists\(\$boarddir\)\s+&&\s+file_exists\(dirname\(__FILE__\)\s+\.\s+'/agreement\.txt'\)\)\s+\$boarddir\s*\=\s*dirname\(__FILE__\);\s+if\s*\(\!file_exists\(\$sourcedir\)\s+&&\s+file_exists\(\$boarddir\s*\.\s*'/Sources'\)\)\s+\$sourcedir\s*\=\s*\$boarddir\s*\.\s*'/Sources';\s+if\s*\(\!file_exists\(\$cachedir\)\s+&&\s+file_exists\(\$boarddir\s*\.\s*'/cache'\)\)\s+\$cachedir\s*\=\s*\$boarddir\s*\.\s*'/cache';
3375
EOT;
3376
3377
	$replacement = <<<'EOT'
3378
# Make sure the paths are correct... at least try to fix them.
3379
if (!file_exists($boarddir) && file_exists(dirname(__FILE__) . '/agreement.txt'))
3380
	$boarddir = dirname(__FILE__);
3381
if (!file_exists($sourcedir) && file_exists($boarddir . '/Sources'))
3382
	$sourcedir = $boarddir . '/Sources';
3383
if (!file_exists($cachedir) && file_exists($boarddir . '/cache'))
3384
	$cachedir = $boarddir . '/cache';
3385
3386
3387
EOT;
3388
3389
	if (preg_match('~' . $regex . '~', $settings) && preg_match('~(#+\s*Error-Catching\s*#+)~', $settings))
3390
	{
3391
		$settings = preg_replace('~' . $regex . '~', '', $settings);
3392
		$settings = preg_replace('~(#+\s*Error-Catching\s*#+)~', $replacement . '$1', $settings);
3393
		$settings = preg_replace('~dirname(__FILE__) . \'/db_last_error.php\'~', '(isset($cachedir) ? $cachedir : dirname(__FILE__)) . \'/db_last_error.php\'', $settings);
3394
3395
		// Blank out the file - done to fix a oddity with some servers.
3396
		file_put_contents(dirname(__FILE__) . '/Settings.php', '');
3397
3398
		file_put_contents(dirname(__FILE__) . '/Settings.php', $settings);
3399
	}
3400
}
3401
3402
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3403
                        Templates are below this point
3404
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
3405
3406
// This is what is displayed if there's any chmod to be done. If not it returns nothing...
3407
function template_chmod()
3408
{
3409
	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...
3410
3411
	// Don't call me twice!
3412
	if (!empty($upcontext['chmod_called']))
3413
		return;
3414
3415
	$upcontext['chmod_called'] = true;
3416
3417
	// Nothing?
3418
	if (empty($upcontext['chmod']['files']) && empty($upcontext['chmod']['ftp_error']))
3419
		return;
3420
3421
	// Was it a problem with Windows?
3422
	if (!empty($upcontext['chmod']['ftp_error']) && $upcontext['chmod']['ftp_error'] == 'total_mess')
3423
	{
3424
		echo '
3425
			<div class="error_message red">
3426
				The following files need to be writable to continue the upgrade. Please ensure the Windows permissions are correctly set to allow this:<br>
3427
				<ul style="margin: 2.5ex; font-family: monospace;">
3428
					<li>' . implode('</li>
3429
					<li>', $upcontext['chmod']['files']) . '</li>
3430
				</ul>
3431
			</div>';
3432
3433
		return false;
3434
	}
3435
3436
	echo '
3437
		<div class="panel">
3438
			<h2>', $txt['upgrade_ftp_login'], '</h2>
3439
			<h3>', $txt['upgrade_ftp_perms'], '</h3>
3440
			<script>
3441
				function warning_popup()
3442
				{
3443
					popup = window.open(\'\',\'popup\',\'height=150,width=400,scrollbars=yes\');
3444
					var content = popup.document;
3445
					content.write(\'<!DOCTYPE html>\n\');
3446
					content.write(\'<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>\n\t<head>\n\t\t<meta name="robots" content="noindex">\n\t\t\');
3447
					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\');
3448
					content.write(\'<div class="windowbg description">\n\t\t\t<h4>', $txt['upgrade_ftp_files'], '</h4>\n\t\t\t\');
3449
					content.write(\'<p>', implode('<br>\n\t\t\t', $upcontext['chmod']['files']), '</p>\n\t\t\t\');';
3450
3451
	if (isset($upcontext['systemos']) && $upcontext['systemos'] == 'linux')
3452
		echo '
3453
					content.write(\'<hr>\n\t\t\t\');
3454
					content.write(\'<p>', $txt['upgrade_ftp_shell'], '</p>\n\t\t\t\');
3455
					content.write(\'<tt># chmod a+w ', implode(' ', $upcontext['chmod']['files']), '</tt>\n\t\t\t\');';
3456
3457
	echo '
3458
					content.write(\'<a href="javascript:self.close();">close</a>\n\t\t</div>\n\t</body>\n</html>\');
3459
					content.close();
3460
				}
3461
			</script>';
3462
3463
	if (!empty($upcontext['chmod']['ftp_error']))
3464
		echo '
3465
			<div class="error_message red">
3466
				', $txt['upgrade_ftp_error'], '<br><br>
3467
				<code>', $upcontext['chmod']['ftp_error'], '</code>
3468
			</div>
3469
			<br>';
3470
3471
	if (empty($upcontext['chmod_in_form']))
3472
		echo '
3473
	<form action="', $upcontext['form_url'], '" method="post">';
3474
3475
	echo '
3476
		<table width="520" border="0" align="center" style="margin-bottom: 1ex;">
3477
			<tr>
3478
				<td width="26%" valign="top" class="textbox"><label for="ftp_server">', $txt['ftp_server'], ':</label></td>
3479
				<td>
3480
					<div style="float: right; margin-right: 1px;">
3481
						<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', '">
3482
					</div>
3483
					<input type="text" size="30" name="ftp_server" id="ftp_server" value="', isset($upcontext['chmod']['server']) ? $upcontext['chmod']['server'] : 'localhost', '" style="width: 70%;">
3484
					<div class="smalltext block">', $txt['ftp_server_info'], '</div>
3485
				</td>
3486
			</tr><tr>
3487
				<td width="26%" valign="top" class="textbox"><label for="ftp_username">', $txt['ftp_username'], ':</label></td>
3488
				<td>
3489
					<input type="text" size="50" name="ftp_username" id="ftp_username" value="', isset($upcontext['chmod']['username']) ? $upcontext['chmod']['username'] : '', '" style="width: 99%;">
3490
					<div class="smalltext block">', $txt['ftp_username_info'], '</div>
3491
				</td>
3492
			</tr><tr>
3493
				<td width="26%" valign="top" class="textbox"><label for="ftp_password">', $txt['ftp_password'], ':</label></td>
3494
				<td>
3495
					<input type="password" size="50" name="ftp_password" id="ftp_password" style="width: 99%;">
3496
					<div class="smalltext block">', $txt['ftp_password_info'], '</div>
3497
				</td>
3498
			</tr><tr>
3499
				<td width="26%" valign="top" class="textbox"><label for="ftp_path">', $txt['ftp_path'], ':</label></td>
3500
				<td style="padding-bottom: 1ex;">
3501
					<input type="text" size="50" name="ftp_path" id="ftp_path" value="', isset($upcontext['chmod']['path']) ? $upcontext['chmod']['path'] : '', '" style="width: 99%;">
3502
					<div class="smalltext block">', !empty($upcontext['chmod']['path']) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'], '</div>
3503
				</td>
3504
			</tr>
3505
		</table>
3506
3507
		<div class="righttext" style="margin: 1ex;"><input type="submit" value="', $txt['ftp_connect'], '" class="button"></div>
3508
	</div>';
3509
3510
	if (empty($upcontext['chmod_in_form']))
3511
		echo '
3512
	</form>';
3513
}
3514
3515
function template_upgrade_above()
3516
{
3517
	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...
3518
3519
	echo '<!DOCTYPE html>
3520
<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>
3521
<head>
3522
	<meta charset="', isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8', '">
3523
	<meta name="robots" content="noindex">
3524
	<title>', $txt['upgrade_upgrade_utility'], '</title>
3525
	<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/index.css?alp21">
3526
	<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/install.css?alp21">
3527
	', $txt['lang_rtl'] == true ? '<link rel="stylesheet" href="' . $settings['default_theme_url'] . '/css/rtl.css?alp21">' : '', '
3528
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
3529
	<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
3530
	<script>
3531
		var smf_scripturl = \'', $upgradeurl, '\';
3532
		var smf_charset = \'', (empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'UTF-8' : $txt['lang_character_set']) : $modSettings['global_character_set']), '\';
3533
		var startPercent = ', $upcontext['overall_percent'], ';
3534
3535
		// This function dynamically updates the step progress bar - and overall one as required.
3536
		function updateStepProgress(current, max, overall_weight)
3537
		{
3538
			// What out the actual percent.
3539
			var width = parseInt((current / max) * 100);
3540
			if (document.getElementById(\'step_progress\'))
3541
			{
3542
				document.getElementById(\'step_progress\').style.width = width + "%";
3543
				setInnerHTML(document.getElementById(\'step_text\'), width + "%");
3544
			}
3545
			if (overall_weight && document.getElementById(\'overall_progress\'))
3546
			{
3547
				overall_width = parseInt(startPercent + width * (overall_weight / 100));
3548
				document.getElementById(\'overall_progress\').style.width = overall_width + "%";
3549
				setInnerHTML(document.getElementById(\'overall_text\'), overall_width + "%");
3550
			}
3551
		}
3552
	</script>
3553
</head>
3554
<body>
3555
	<div id="footerfix">
3556
	<div id="header">
3557
		<h1 class="forumtitle">', $txt['upgrade_upgrade_utility'], '</h1>
3558
		<img id="smflogo" src="', $settings['default_theme_url'], '/images/smflogo.svg" alt="Simple Machines Forum" title="Simple Machines Forum">
3559
	</div>
3560
	<div id="wrapper">
3561
		<div id="upper_section">
3562
			<div id="inner_section">
3563
				<div id="inner_wrap">
3564
				</div>
3565
			</div>
3566
		</div>
3567
		<div id="content_section">
3568
			<div id="main_content_section">
3569
				<div id="main_steps">
3570
					<h2>', $txt['upgrade_progress'], '</h2>
3571
					<ul>';
3572
3573 View Code Duplication
	foreach ($upcontext['steps'] as $num => $step)
3574
		echo '
3575
						<li class="', $num < $upcontext['current_step'] ? 'stepdone' : ($num == $upcontext['current_step'] ? 'stepcurrent' : 'stepwaiting'), '">', $txt['upgrade_step'], ' ', $step[0], ': ', $step[1], '</li>';
3576
3577
	echo '
3578
					</ul>
3579
				</div><!-- #main_steps -->
3580
3581
				<div id="progress_bar">
3582
					<div id="overall_text">', $upcontext['overall_percent'], '%</div>
3583
					<div id="overall_progress" style="width: ', $upcontext['overall_percent'], '%;">
3584
						<span>', $txt['upgrade_overall_progress'], '</span>
3585
					</div>
3586
				</div>';
3587
3588
	if (isset($upcontext['step_progress']))
3589
		echo '
3590
				<br>
3591
				<br>
3592
				<div id="progress_bar_step">
3593
					<div id="step_text">', $upcontext['step_progress'], '%</div>
3594
					<div id="step_progress" style="width: ', $upcontext['step_progress'], '%;background-color: #ffd000;">
3595
						<span>', $txt['upgrade_step_progress'], '</span>
3596
					</div>
3597
				</div>';
3598
3599
	echo '
3600
				<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>
3601
				<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', ';">
3602
					<div id="substep_text" style="color: #000; position: absolute; margin-left: -5em;">', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : '', '%</div>
3603
					<div id="substep_progress" style="width: ', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%; height: 12pt; z-index: 1; background-color: #eebaf4;">&nbsp;</div>
3604
				</div>';
3605
3606
	// How long have we been running this?
3607
	$elapsed = time() - $upcontext['started'];
3608
	$mins = (int) ($elapsed / 60);
3609
	$seconds = $elapsed - $mins * 60;
3610
	echo '
3611
				<br> <br> <br> <br> <br>
3612
				<div class="smalltext" style="padding: 5px; text-align: center;">
3613
					<br>', $txt['upgrade_time_elapsed'], ':
3614
					<span id="mins_elapsed">', $mins, '</span> ', $txt['upgrade_time_mins'], ', <span id="secs_elapsed">', $seconds, '</span> ', $txt['upgrade_time_secs'], '.
3615
				</div>';
3616
	echo '
3617
			</div><!-- #main_content_section -->
3618
		</div><!-- #content_section -->
3619
		<div id="main_screen" class="clear">
3620
			<h2>', $upcontext['page_title'], '</h2>
3621
			<div class="panel">';
3622
}
3623
3624
function template_upgrade_below()
3625
{
3626
	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...
3627
3628
	if (!empty($upcontext['pause']))
3629
		echo '
3630
						<em>', $txt['upgrade_incomplete'], '.</em><br>
3631
3632
						<h2 style="margin-top: 2ex;">', $txt['upgrade_not_quite_done'], '</h2>
3633
						<h3>
3634
							', $txt['upgrade_paused_overload'], '
3635
						</h3>';
3636
3637
	if (!empty($upcontext['custom_warning']))
3638
		echo '
3639
						<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3640
							<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3641
							<strong style="text-decoration: underline;">', $txt['upgrade_note'], '</strong><br>
3642
							<div style="padding-left: 6ex;">', $upcontext['custom_warning'], '</div>
3643
						</div>';
3644
3645
	echo '
3646
						<div class="righttext" style="margin: 1ex;">';
3647
3648
	if (!empty($upcontext['continue']))
3649
		echo '
3650
						<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '"', $upcontext['continue'] == 2 ? ' disabled' : '', ' class="button">';
3651
	if (!empty($upcontext['skip']))
3652
		echo '
3653
						<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="dontSubmit = true; document.getElementById(\'contbutt\').disabled = \'disabled\'; return true;" class="button">';
3654
3655
	echo '
3656
					</div>
3657
				</form>
3658
			</div><!-- .panel -->
3659
		</div><!-- #main_screen -->
3660
	</div><!-- #wrapper -->
3661
	</div><!-- #footerfix -->
3662
	<div id="footer">
3663
		<ul>
3664
			<li class="copyright"><a href="https://www.simplemachines.org/" title="Simple Machines Forum" target="_blank" rel="noopener">SMF &copy; 2018, Simple Machines</a></li>
3665
		</ul>
3666
	</div>';
3667
3668
	// Are we on a pause?
3669
	if (!empty($upcontext['pause']))
3670
	{
3671
		echo '
3672
	<script>
3673
		window.onload = doAutoSubmit;
3674
		var countdown = 3;
3675
		var dontSubmit = false;
3676
3677
		function doAutoSubmit()
3678
		{
3679
			if (countdown == 0 && !dontSubmit)
3680
				document.upform.submit();
3681
			else if (countdown == -1)
3682
				return;
3683
3684
			document.getElementById(\'contbutt\').value = "', $txt['upgrade_continue'], ' (" + countdown + ")";
3685
			countdown--;
3686
3687
			setTimeout("doAutoSubmit();", 1000);
3688
		}
3689
	</script>';
3690
	}
3691
3692
	echo '
3693
</body>
3694
</html>';
3695
}
3696
3697
function template_xml_above()
3698
{
3699
	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...
3700
3701
	echo '<', '?xml version="1.0" encoding="UTF-8"?', '>
3702
	<smf>';
3703
3704
	if (!empty($upcontext['get_data']))
3705
		foreach ($upcontext['get_data'] as $k => $v)
3706
			echo '
3707
		<get key="', $k, '">', $v, '</get>';
3708
}
3709
3710
function template_xml_below()
3711
{
3712
	echo '
3713
	</smf>';
3714
}
3715
3716
function template_error_message()
3717
{
3718
	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...
3719
3720
	echo '
3721
	<div class="error_message red">
3722
		', $upcontext['error_msg'], '
3723
		<br>
3724
		<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 3724

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...
3725
	</div>';
3726
}
3727
3728
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 (L2012-2061) 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...
3729
{
3730
	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...
3731
3732
	echo '
3733
				<script src="https://www.simplemachines.org/smf/current-version.js?version=' . SMF_VERSION . '"></script>
3734
				
3735
				<h3>', sprintf($txt['upgrade_ready_proceed'], SMF_VERSION), '</h3>
3736
				<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">
3737
					<input type="hidden" name="', $upcontext['login_token_var'], '" value="', $upcontext['login_token'], '">
3738
					<div id="version_warning" style="margin: 2ex; padding: 2ex; border: 2px dashed #a92174; color: black; background-color: #fbbbe2; display: none;">
3739
						<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3740
						<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3741
						<div style="padding-left: 6ex;">
3742
							', sprintf($txt['upgrade_warning_out_of_date'], SMF_VERSION, 'https://www.simplemachines.org'), '
3743
						</div>
3744
					</div>';
3745
3746
	$upcontext['chmod_in_form'] = true;
3747
	template_chmod();
3748
3749
	// For large, pre 1.1 RC2 forums give them a warning about the possible impact of this upgrade!
3750
	if ($upcontext['is_large_forum'])
3751
		echo '
3752
					<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3753
						<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3754
						<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3755
						<div style="padding-left: 6ex;">
3756
							', $txt['upgrade_warning_lots_data'], '
3757
						</div>
3758
					</div>';
3759
3760
	// A warning message?
3761
	if (!empty($upcontext['warning']))
3762
		echo '
3763
					<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3764
						<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3765
						<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3766
						<div style="padding-left: 6ex;">
3767
							', $upcontext['warning'], '
3768
						</div>
3769
					</div>';
3770
3771
	// Paths are incorrect?
3772
	echo '
3773
					<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">
3774
						<div style="float: left; width: 2ex; font-size: 2em; color: black;">!!</div>
3775
						<strong style="text-decoration: underline;">', $txt['upgrade_critical_error'], '</strong><br>
3776
						<div style="padding-left: 6ex;">
3777
							', sprintf($txt['upgrade_error_script_js'], 'https://www.simplemachines.org'), '
3778
						</div>
3779
					</div>';
3780
3781
	// Is there someone already doing this?
3782
	if (!empty($upcontext['user']['id']) && (time() - $upcontext['started'] < 72600 || time() - $upcontext['updated'] < 3600))
3783
	{
3784
		$ago = time() - $upcontext['started'];
3785
		if ($ago < 60)
3786
			$ago = $ago . ' seconds';
3787
		elseif ($ago < 3600)
3788
			$ago = (int) ($ago / 60) . ' minutes';
3789
		else
3790
			$ago = (int) ($ago / 3600) . ' hours';
3791
3792
		$active = time() - $upcontext['updated'];
3793
		if ($active < 60)
3794
			$updated = $active . ' seconds';
3795
		elseif ($active < 3600)
3796
			$updated = (int) ($active / 60) . ' minutes';
3797
		else
3798
			$updated = (int) ($active / 3600) . ' hours';
3799
3800
		echo '
3801
					<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3802
						<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3803
						<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3804
						<div style="padding-left: 6ex;">
3805
							&quot;', $upcontext['user']['name'], '&quot; has been running the upgrade script for the last ', $ago, ' - and was last active ', $updated, ' ago.';
3806
3807
		if ($active < 600)
3808
			echo '
3809
						', $txt['upgrade_run_script'], ' ', $upcontext['user']['name'],' ', $txt['upgrade_run_script2'], '';
3810
3811
		if ($active > $upcontext['inactive_timeout'])
3812
			echo '
3813
						<br><br>',$txt['upgrade_run'], '';
3814
		else
3815
			echo '
3816
						<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!');
3817
3818
		echo '
3819
						</div>
3820
					</div>';
3821
	}
3822
3823
	echo '
3824
					<strong>', $txt['upgrade_admin_login'], ' ', $disable_security ? '(DISABLED)' : '', '</strong>
3825
					<h3>', $txt['upgrade_sec_login'], '</h3>
3826
					<table>
3827
						<tr valign="top">
3828
							<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>', $txt['upgrade_username'], '</strong></td>
3829
							<td>
3830
								<input type="text" name="user" value="', !empty($upcontext['username']) ? $upcontext['username'] : '', '"', $disable_security ? ' disabled' : '', '>';
3831
3832
	if (!empty($upcontext['username_incorrect']))
3833
		echo '
3834
								<div class="smalltext" style="color: red;">', $txt['upgrade_wrong_username'], '</div>';
3835
3836
	echo '
3837
							</td>
3838
						</tr>
3839
						<tr valign="top">
3840
							<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>', $txt['upgrade_password'], '</strong></td>
3841
							<td>
3842
								<input type="password" name="passwrd" value=""', $disable_security ? ' disabled' : '', '>
3843
								<input type="hidden" name="hash_passwrd" value="">';
3844
3845
	if (!empty($upcontext['password_failed']))
3846
		echo '
3847
								<div class="smalltext" style="color: red;">', $txt['upgrade_wrong_password'], '</div>';
3848
3849
	echo '
3850
							</td>
3851
						</tr>';
3852
3853
	// Can they continue?
3854
	if (!empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] >= $upcontext['inactive_timeout'] && $upcontext['user']['step'] > 1)
3855
	{
3856
		echo '
3857
						<tr>
3858
							<td colspan="2">
3859
								<label for="cont"><input type="checkbox" id="cont" name="cont" checked>', $txt['upgrade_continue_step'], '</label>
3860
							</td>
3861
						</tr>';
3862
	}
3863
3864
	echo '
3865
					</table>
3866
					<br>
3867
					<span class="smalltext">
3868
						', $txt['upgrade_bypass'], '
3869
					</span>
3870
					<input type="hidden" name="login_attempt" id="login_attempt" value="1">
3871
					<input type="hidden" name="js_works" id="js_works" value="0">';
3872
3873
	// Say we want the continue button!
3874
	$upcontext['continue'] = !empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] < $upcontext['inactive_timeout'] ? 2 : 1;
3875
3876
	// This defines whether javascript is going to work elsewhere :D
3877
	echo '
3878
					<script>
3879
						if (\'XMLHttpRequest\' in window && document.getElementById(\'js_works\'))
3880
							document.getElementById(\'js_works\').value = 1;
3881
3882
						// Latest version?
3883
						function smfCurrentVersion()
3884
						{
3885
							var smfVer, yourVer;
3886
3887
							if (!(\'smfVersion\' in window))
3888
								return;
3889
3890
							window.smfVersion = window.smfVersion.replace(/SMF\s?/g, \'\');
3891
3892
							smfVer = document.getElementById(\'smfVersion\');
3893
							yourVer = document.getElementById(\'yourVersion\');
3894
3895
							setInnerHTML(smfVer, window.smfVersion);
3896
3897
							var currentVersion = getInnerHTML(yourVer);
3898
							if (currentVersion < window.smfVersion)
3899
								document.getElementById(\'version_warning\').style.display = \'\';
3900
						}
3901
						addLoadEvent(smfCurrentVersion);
3902
3903
						// This checks that the script file even exists!
3904
						if (typeof(smfSelectText) == \'undefined\')
3905
							document.getElementById(\'js_script_missing_error\').style.display = \'\';
3906
3907
					</script>';
3908
}
3909
3910
function template_upgrade_options()
3911
{
3912
	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...
3913
3914
	echo '
3915
				<h3>', $txt['upgrade_areyouready'], '</h3>
3916
				<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">';
3917
3918
	// Warning message?
3919
	if (!empty($upcontext['upgrade_options_warning']))
3920
		echo '
3921
				<div style="margin: 1ex; padding: 1ex; border: 1px dashed #cc3344; color: black; background-color: #ffe4e9;">
3922
					<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3923
					<strong style="text-decoration: underline;">Warning!</strong><br>
3924
					<div style="padding-left: 4ex;">
3925
						', $upcontext['upgrade_options_warning'], '
3926
					</div>
3927
				</div>';
3928
3929
	echo '
3930
				<table>
3931
					<tr valign="top">
3932
						<td width="2%">
3933
							<input type="checkbox" name="backup" id="backup" value="1">
3934
						</td>
3935
						<td width="100%">
3936
							<label for="backup">', $txt['upgrade_backup_table'], ' &quot;backup_' . $db_prefix . '&quot;.</label> (', $txt['upgrade_recommended'], ')
3937
						</td>
3938
					</tr>
3939
					<tr valign="top">
3940
						<td width="2%">
3941
							<input type="checkbox" name="maint" id="maint" value="1" checked>
3942
						</td>
3943
						<td width="100%">
3944
							<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>
3945
							<div id="mainmess" style="display: none;">
3946
								<strong class="smalltext">', $txt['upgrade_maintenance_title'], ' </strong><br>
3947
								<input type="text" name="maintitle" size="30" value="', htmlspecialchars($mtitle), '"><br>
3948
								<strong class="smalltext">', $txt['upgrade_maintenace_message'], ' </strong><br>
3949
								<textarea name="mainmessage" rows="3" cols="50">', htmlspecialchars($mmessage), '</textarea>
3950
							</div>
3951
						</td>
3952
					</tr>
3953
					<tr valign="top">
3954
						<td width="2%">
3955
							<input type="checkbox" name="debug" id="debug" value="1">
3956
						</td>
3957
						<td width="100%">
3958
							<label for="debug">'.$txt['upgrade_debug_info'], '</label>
3959
						</td>
3960
					</tr>
3961
					<tr valign="top">
3962
						<td width="2%">
3963
							<input type="checkbox" name="empty_error" id="empty_error" value="1">
3964
						</td>
3965
						<td width="100%">
3966
							<label for="empty_error">', $txt['upgrade_empty_errlog'], '</label>
3967
						</td>
3968
					</tr>';
3969
3970
	if (!empty($upcontext['karma_installed']['good']) || !empty($upcontext['karma_installed']['bad']))
3971
		echo '
3972
					<tr valign="top">
3973
						<td width="2%">
3974
							<input type="checkbox" name="delete_karma" id="delete_karma" value="1">
3975
						</td>
3976
						<td width="100%">
3977
							<label for="delete_karma">', $txt['upgrade_delete_karma'], '</label>
3978
						</td>
3979
					</tr>';
3980
3981
	echo '
3982
					<tr valign="top">
3983
						<td width="2%">
3984
							<input type="checkbox" name="stats" id="stats" value="1"', empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']) ? '' : ' checked="checked"', '>
3985
						</td>
3986
						<td width="100%">
3987
							<label for="stat">
3988
								', $txt['upgrade_stats_collection'], '<br>
3989
								<span class="smalltext">', sprintf($txt['upgrade_stats_info'], 'https://www.simplemachines.org/about/stats.php'), '</a></span>
3990
							</label>
3991
						</td>
3992
					</tr>
3993
					<tr valign="top">
3994
						<td width="2%">
3995
							<input type="checkbox" name="migrateSettings" id="migrateSettings" value="1"', empty($upcontext['migrateSettingsNeeded']) ? '' : ' checked="checked"', '>
3996
						</td>
3997
						<td width="100%">
3998
							<label for="migrateSettings">
3999
								', $txt['upgrade_migrate_settings_file'], '
4000
							</label>
4001
						</td>
4002
					</tr>
4003
				</table>
4004
				<input type="hidden" name="upcont" value="1">';
4005
4006
	// We need a normal continue button here!
4007
	$upcontext['continue'] = 1;
4008
}
4009
4010
// Template for the database backup tool/
4011
function template_backup_database()
4012
{
4013
	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...
4014
4015
	echo '
4016
				<h3>', $txt['upgrade_wait'], '</h3>';
4017
4018
	echo '
4019
				<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4020
					<input type="hidden" name="backup_done" id="backup_done" value="0">
4021
					<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
4022
					<div id="debug_section" style="height: ', ($is_debug ? '115' : '12') , 'px; overflow: auto;">
4023
						<span id="debuginfo"></span>
4024
					</div>';
4025
4026
	// Dont any tables so far?
4027 View Code Duplication
	if (!empty($upcontext['previous_tables']))
4028
		foreach ($upcontext['previous_tables'] as $table)
4029
			echo '
4030
					<br>', $txt['upgrade_completed_table'], ' &quot;', $table, '&quot;.';
4031
4032
	echo '
4033
					<h3 id="current_tab_div">', $txt['upgrade_current_table'], ' &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
4034
					<br>
4035
					<span id="commess" style="font-weight: bold; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">Backup Complete! Click Continue to Proceed.</span>';
4036
4037
	// Continue please!
4038
	$upcontext['continue'] = $support_js ? 2 : 1;
4039
4040
	// If javascript allows we want to do this using XML.
4041 View Code Duplication
	if ($support_js)
4042
	{
4043
		echo '
4044
					<script>
4045
						var lastTable = ', $upcontext['cur_table_num'], ';
4046
						function getNextTables()
4047
						{
4048
							getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4049
						}
4050
4051
						// Got an update!
4052
						function onBackupUpdate(oXMLDoc)
4053
						{
4054
							var sCurrentTableName = "";
4055
							var iTableNum = 0;
4056
							var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4057
							for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4058
								sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4059
							iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4060
4061
							// Update the page.
4062
							setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4063
							setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4064
							lastTable = iTableNum;
4065
							updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4066
4067
		// If debug flood the screen.
4068
		if ($is_debug)
4069
			echo '
4070
							setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4071
4072
							if (document.getElementById(\'debug_section\').scrollHeight)
4073
								document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4074
4075
		echo '
4076
							// Get the next update...
4077
							if (iTableNum == ', $upcontext['table_count'], ')
4078
							{
4079
								document.getElementById(\'commess\').style.display = "";
4080
								document.getElementById(\'current_tab_div\').style.display = "none";
4081
								document.getElementById(\'contbutt\').disabled = 0;
4082
								document.getElementById(\'backup_done\').value = 1;
4083
							}
4084
							else
4085
								getNextTables();
4086
						}
4087
						getNextTables();
4088
					//# sourceURL=dynamicScript-bkup.js
4089
					</script>';
4090
	}
4091
}
4092
4093
function template_backup_xml()
4094
{
4095
	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...
4096
4097
	echo '
4098
		<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4099
}
4100
4101
// Here is the actual "make the changes" template!
4102
function template_database_changes()
4103
{
4104
	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...
4105
4106
	if (empty($is_debug) && !empty($upcontext['upgrade_status']['debug']))
4107
		$is_debug = true;
4108
4109
	echo '
4110
				<h3>', $txt['upgrade_db_changes'], '</h3>
4111
				<h4 style="font-style: italic;">', $txt['upgrade_db_patient'], '</h4>';
4112
4113
	echo '
4114
				<form action="', $upcontext['form_url'], '&amp;filecount=', $upcontext['file_count'], '" name="upform" id="upform" method="post">
4115
					<input type="hidden" name="database_done" id="database_done" value="0">';
4116
4117
	// No javascript looks rubbish!
4118
	if (!$support_js)
4119
	{
4120
		foreach ($upcontext['actioned_items'] as $num => $item)
4121
		{
4122
			if ($num != 0)
4123
				echo ' Successful!';
4124
			echo '<br>' . $item;
4125
		}
4126 View Code Duplication
		if (!empty($upcontext['changes_complete']))
4127
		{
4128
			if ($is_debug)
4129
			{
4130
				$active = time() - $upcontext['started'];
4131
				$hours = floor($active / 3600);
4132
				$minutes = intval(($active / 60) % 60);
4133
				$seconds = intval($active % 60);
4134
4135
				$totalTime = '';
4136
				if ($hours > 0)
4137
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
4138
				if ($minutes > 0)
4139
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
4140
				if ($seconds > 0)
4141
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
4142
			}
4143
4144
			if ($is_debug && !empty($totalTime))
4145
				echo ' Successful! Completed in ', $totalTime, '<br><br>';
4146
			else
4147
				echo ' Successful!<br><br>';
4148
4149
			echo '
4150
					<span id="commess" style="font-weight: bold;">', $txt['upgrade_db_complete'], '</span><br>';
4151
		}
4152
	}
4153
	else
4154
	{
4155
		// Tell them how many files we have in total.
4156
		if ($upcontext['file_count'] > 1)
4157
			echo '
4158
					<strong id="info1">', $txt['upgrade_script'], ' <span id="file_done">', $upcontext['cur_file_num'], '</span> of ', $upcontext['file_count'], '.</strong>';
4159
4160
		echo '
4161
					<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>
4162
					<br>
4163
					<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>';
4164
4165 View Code Duplication
		if ($is_debug)
4166
		{
4167
			if ($upcontext['current_debug_item_num'] == $upcontext['debug_items'])
4168
			{
4169
				$active = time() - $upcontext['started'];
4170
				$hours = floor($active / 3600);
4171
				$minutes = intval(($active / 60) % 60);
4172
				$seconds = intval($active % 60);
4173
4174
				$totalTime = '';
4175
				if ($hours > 0)
4176
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
4177
				if ($minutes > 0)
4178
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
4179
				if ($seconds > 0)
4180
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
4181
			}
4182
4183
			echo '
4184
					<br>
4185
					<span id="upgradeCompleted">';
4186
4187
			if (!empty($totalTime))
4188
				echo 'Completed in ', $totalTime, '<br>';
4189
4190
			echo '
4191
					</span>
4192
					<div id="debug_section" style="height: 59px; overflow: auto;">
4193
						<span id="debuginfo"></span>
4194
					</div>';
4195
		}
4196
	}
4197
4198
	// Place for the XML error message.
4199
	echo '
4200
					<div id="error_block" style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9; display: ', empty($upcontext['error_message']) ? 'none' : '', ';">
4201
						<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4202
						<strong style="text-decoration: underline;">', $txt['upgrade_error'], '</strong><br>
4203
						<div style="padding-left: 6ex;" id="error_message">', isset($upcontext['error_message']) ? $upcontext['error_message'] : $txt['upgrade_unknown_error'], '</div>
4204
					</div>';
4205
4206
	// We want to continue at some point!
4207
	$upcontext['continue'] = $support_js ? 2 : 1;
4208
4209
	// If javascript allows we want to do this using XML.
4210
	if ($support_js)
4211
	{
4212
		echo '
4213
					<script>
4214
						var lastItem = ', $upcontext['current_debug_item_num'], ';
4215
						var sLastString = "', strtr($upcontext['current_debug_item_name'], array('"' => '&quot;')), '";
4216
						var iLastSubStepProgress = -1;
4217
						var curFile = ', $upcontext['cur_file_num'], ';
4218
						var totalItems = 0;
4219
						var prevFile = 0;
4220
						var retryCount = 0;
4221
						var testvar = 0;
4222
						var timeOutID = 0;
4223
						var getData = "";
4224
						var debugItems = ', $upcontext['debug_items'], ';';
4225
4226
		if ($is_debug)
4227
			echo '
4228
						var upgradeStartTime = ' . $upcontext['started'] . ';';
4229
4230
		echo '
4231
						function getNextItem()
4232
						{
4233
							// We want to track this...
4234
							if (timeOutID)
4235
								clearTimeout(timeOutID);
4236
							timeOutID = window.setTimeout("retTimeout()", ', (10 * $timeLimitThreshold), '000);
4237
4238
							getXMLDocument(\'', $upcontext['form_url'], '&xml&filecount=', $upcontext['file_count'], '&substep=\' + lastItem + getData, onItemUpdate);
4239
						}
4240
4241
						// Got an update!
4242
						function onItemUpdate(oXMLDoc)
4243
						{
4244
							var sItemName = "";
4245
							var sDebugName = "";
4246
							var iItemNum = 0;
4247
							var iSubStepProgress = -1;
4248
							var iDebugNum = 0;
4249
							var bIsComplete = 0;
4250
							getData = "";
4251
4252
							// We\'ve got something - so reset the timeout!
4253
							if (timeOutID)
4254
								clearTimeout(timeOutID);
4255
4256
							// Assume no error at this time...
4257
							document.getElementById("error_block").style.display = "none";
4258
4259
							// Are we getting some duff info?
4260
							if (!oXMLDoc.getElementsByTagName("item")[0])
4261
							{
4262
								// Too many errors?
4263
								if (retryCount > 15)
4264
								{
4265
									document.getElementById("error_block").style.display = "";
4266
									setInnerHTML(document.getElementById("error_message"), "Error retrieving information on step: " + (sDebugName == "" ? sLastString : sDebugName));';
4267
4268
	if ($is_debug)
4269
		echo '
4270
									setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4271
4272
	echo '
4273
								}
4274
								else
4275
								{
4276
									retryCount++;
4277
									getNextItem();
4278
								}
4279
								return false;
4280
							}
4281
4282
							// Never allow loops.
4283
							if (curFile == prevFile)
4284
							{
4285
								retryCount++;
4286
								if (retryCount > 10)
4287
								{
4288
									document.getElementById("error_block").style.display = "";
4289
									setInnerHTML(document.getElementById("error_message"), "Upgrade script appears to be going into a loop - step: " + sDebugName);';
4290
4291
	if ($is_debug)
4292
		echo '
4293
									setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4294
4295
	echo '
4296
								}
4297
							}
4298
							retryCount = 0;
4299
4300
							for (var i = 0; i < oXMLDoc.getElementsByTagName("item")[0].childNodes.length; i++)
4301
								sItemName += oXMLDoc.getElementsByTagName("item")[0].childNodes[i].nodeValue;
4302
							for (var i = 0; i < oXMLDoc.getElementsByTagName("debug")[0].childNodes.length; i++)
4303
								sDebugName += oXMLDoc.getElementsByTagName("debug")[0].childNodes[i].nodeValue;
4304
							for (var i = 0; i < oXMLDoc.getElementsByTagName("get").length; i++)
4305
							{
4306
								getData += "&" + oXMLDoc.getElementsByTagName("get")[i].getAttribute("key") + "=";
4307
								for (var j = 0; j < oXMLDoc.getElementsByTagName("get")[i].childNodes.length; j++)
4308
								{
4309
									getData += oXMLDoc.getElementsByTagName("get")[i].childNodes[j].nodeValue;
4310
								}
4311
							}
4312
4313
							iItemNum = oXMLDoc.getElementsByTagName("item")[0].getAttribute("num");
4314
							iDebugNum = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("num"));
4315
							bIsComplete = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("complete"));
4316
							iSubStepProgress = parseFloat(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("percent"));
4317
							sLastString = sDebugName + " (Item: " + iDebugNum + ")";
4318
4319
							curFile = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("num"));
4320
							debugItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("debug_items"));
4321
							totalItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("items"));
4322
4323
							// If we have an error we haven\'t completed!
4324
							if (oXMLDoc.getElementsByTagName("error")[0] && bIsComplete)
4325
								iDebugNum = lastItem;
4326
4327
							// Do we have the additional progress bar?
4328
							if (iSubStepProgress != -1)
4329
							{
4330
								document.getElementById("substep_bar_div").style.display = "";
4331
								document.getElementById("substep_bar_div2").style.display = "";
4332
								document.getElementById("substep_progress").style.width = iSubStepProgress + "%";
4333
								setInnerHTML(document.getElementById("substep_text"), iSubStepProgress + "%");
4334
								setInnerHTML(document.getElementById("substep_bar_div"), sDebugName.replace(/\./g, "") + ":");
4335
							}
4336
							else
4337
							{
4338
								document.getElementById("substep_bar_div").style.display = "none";
4339
								document.getElementById("substep_bar_div2").style.display = "none";
4340
							}
4341
4342
							// Move onto the next item?
4343
							if (bIsComplete)
4344
								lastItem = iDebugNum;
4345
							else
4346
								lastItem = iDebugNum - 1;
4347
4348
							// Are we finished?
4349
							if (bIsComplete && iDebugNum == -1 && curFile >= ', $upcontext['file_count'], ')
4350
							{';
4351
4352
		if ($is_debug)
4353
			echo '
4354
								document.getElementById(\'debug_section\').style.display = "none";
4355
4356
								var upgradeFinishedTime = parseInt(oXMLDoc.getElementsByTagName("curtime")[0].childNodes[0].nodeValue);
4357
								var diffTime = upgradeFinishedTime - upgradeStartTime;
4358
								var diffHours = Math.floor(diffTime / 3600);
4359
								var diffMinutes = parseInt((diffTime / 60) % 60);
4360
								var diffSeconds = parseInt(diffTime % 60);
4361
4362
								var totalTime = "";
4363
								if (diffHours > 0)
4364
									totalTime = totalTime + diffHours + " hour" + (diffHours > 1 ? "s" : "") + " ";
4365
								if (diffMinutes > 0)
4366
									totalTime = totalTime + diffMinutes + " minute" + (diffMinutes > 1 ? "s" : "") + " ";
4367
								if (diffSeconds > 0)
4368
									totalTime = totalTime + diffSeconds + " second" + (diffSeconds > 1 ? "s" : "");
4369
4370
								setInnerHTML(document.getElementById("upgradeCompleted"), "Completed in " + totalTime);';
4371
4372
		echo '
4373
4374
								document.getElementById(\'commess\').style.display = "";
4375
								document.getElementById(\'contbutt\').disabled = 0;
4376
								document.getElementById(\'database_done\').value = 1;';
4377
4378
		if ($upcontext['file_count'] > 1)
4379
			echo '
4380
								document.getElementById(\'info1\').style.display = "none";';
4381
4382
		echo '
4383
								document.getElementById(\'info2\').style.display = "none";
4384
								updateStepProgress(100, 100, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4385
								return true;
4386
							}
4387
							// Was it the last step in the file?
4388
							else if (bIsComplete && iDebugNum == -1)
4389
							{
4390
								lastItem = 0;
4391
								prevFile = curFile;';
4392
4393
		if ($is_debug)
4394
			echo '
4395
								setOuterHTML(document.getElementById(\'debuginfo\'), \'Moving to next script file...done<br><span id="debuginfo"><\' + \'/span>\');';
4396
4397
		echo '
4398
								getNextItem();
4399
								return true;
4400
							}';
4401
4402
		// If debug scroll the screen.
4403
		if ($is_debug)
4404
			echo '
4405
							if (iLastSubStepProgress == -1)
4406
							{
4407
								// Give it consistent dots.
4408
								dots = sDebugName.match(/\./g);
4409
								numDots = dots ? dots.length : 0;
4410
								for (var i = numDots; i < 3; i++)
4411
									sDebugName += ".";
4412
								setOuterHTML(document.getElementById(\'debuginfo\'), sDebugName + \'<span id="debuginfo"><\' + \'/span>\');
4413
							}
4414
							iLastSubStepProgress = iSubStepProgress;
4415
4416
							if (bIsComplete)
4417
								setOuterHTML(document.getElementById(\'debuginfo\'), \'done<br><span id="debuginfo"><\' + \'/span>\');
4418
							else
4419
								setOuterHTML(document.getElementById(\'debuginfo\'), \'...<span id="debuginfo"><\' + \'/span>\');
4420
4421
							if (document.getElementById(\'debug_section\').scrollHeight)
4422
								document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4423
4424
		echo '
4425
							// Update the page.
4426
							setInnerHTML(document.getElementById(\'item_num\'), iItemNum);
4427
							setInnerHTML(document.getElementById(\'cur_item_name\'), sItemName);';
4428
4429
		if ($upcontext['file_count'] > 1)
4430
		{
4431
			echo '
4432
							setInnerHTML(document.getElementById(\'file_done\'), curFile);
4433
							setInnerHTML(document.getElementById(\'item_count\'), totalItems);';
4434
		}
4435
4436
		echo '
4437
							// Is there an error?
4438
							if (oXMLDoc.getElementsByTagName("error")[0])
4439
							{
4440
								var sErrorMsg = "";
4441
								for (var i = 0; i < oXMLDoc.getElementsByTagName("error")[0].childNodes.length; i++)
4442
									sErrorMsg += oXMLDoc.getElementsByTagName("error")[0].childNodes[i].nodeValue;
4443
								document.getElementById("error_block").style.display = "";
4444
								setInnerHTML(document.getElementById("error_message"), sErrorMsg);
4445
								return false;
4446
							}
4447
4448
							// Get the progress bar right.
4449
							barTotal = debugItems * ', $upcontext['file_count'], ';
4450
							barDone = (debugItems * (curFile - 1)) + lastItem;
4451
4452
							updateStepProgress(barDone, barTotal, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4453
4454
							// Finally - update the time here as it shows the server is responding!
4455
							curTime = new Date();
4456
							iElapsed = (curTime.getTime() / 1000 - ', $upcontext['started'], ');
4457
							mins = parseInt(iElapsed / 60);
4458
							secs = parseInt(iElapsed - mins * 60);
4459
							setInnerHTML(document.getElementById("mins_elapsed"), mins);
4460
							setInnerHTML(document.getElementById("secs_elapsed"), secs);
4461
4462
							getNextItem();
4463
							return true;
4464
						}
4465
4466
						// What if we timeout?!
4467
						function retTimeout(attemptAgain)
4468
						{
4469
							// Oh noes...
4470
							if (!attemptAgain)
4471
							{
4472
								document.getElementById("error_block").style.display = "";
4473
								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");
4474
							}
4475
							else
4476
							{
4477
								document.getElementById("error_block").style.display = "none";
4478
								getNextItem();
4479
							}
4480
						}';
4481
4482
		// Start things off assuming we've not errored.
4483
		if (empty($upcontext['error_message']))
4484
			echo '
4485
						getNextItem();';
4486
4487
		echo '
4488
					//# sourceURL=dynamicScript-dbch.js
4489
					</script>';
4490
	}
4491
	return;
4492
}
4493
4494
function template_database_xml()
4495
{
4496
	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...
4497
4498
	echo '
4499
	<file num="', $upcontext['cur_file_num'], '" items="', $upcontext['total_items'], '" debug_items="', $upcontext['debug_items'], '">', $upcontext['cur_file_name'], '</file>
4500
	<item num="', $upcontext['current_item_num'], '">', $upcontext['current_item_name'], '</item>
4501
	<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>';
4502
4503
	if (!empty($upcontext['error_message']))
4504
		echo '
4505
	<error>', $upcontext['error_message'], '</error>';
4506
4507
	if (!empty($upcontext['error_string']))
4508
		echo '
4509
	<sql>', $upcontext['error_string'], '</sql>';
4510
4511
	if ($is_debug)
4512
		echo '
4513
	<curtime>', time(), '</curtime>';
4514
}
4515
4516
// Template for the UTF-8 conversion step. Basically a copy of the backup stuff with slight modifications....
4517
function template_convert_utf8()
4518
{
4519
	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...
4520
4521
	echo '
4522
				<h3>', $txt['upgrade_wait2'], '</h3>
4523
				<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4524
					<input type="hidden" name="utf8_done" id="utf8_done" value="0">
4525
					<strong>', $txt['upgrade_completed'], ' <span id="tab_done">', $upcontext['cur_table_num'], '</span> ', $txt['upgrade_outof'], ' ', $upcontext['table_count'], ' ', $txt['upgrade_tables'], '</strong>
4526
					<div id="debug_section" style="height: ', ($is_debug ? '97' : '12') , 'px; overflow: auto;">
4527
						<span id="debuginfo"></span>
4528
					</div>';
4529
4530
	// Done any tables so far?
4531 View Code Duplication
	if (!empty($upcontext['previous_tables']))
4532
		foreach ($upcontext['previous_tables'] as $table)
4533
			echo '
4534
					<br>', $txt['upgrade_completed_table'], ' &quot;', $table, '&quot;.';
4535
4536
	echo '
4537
					<h3 id="current_tab_div">', $txt['upgrade_current_table'], ' &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>';
4538
4539
	// If we dropped their index, let's let them know
4540
	if ($upcontext['dropping_index'])
4541
		echo '
4542
					<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>';
4543
4544
	// Completion notification
4545
	echo '
4546
					<br><span id="commess" style="font-weight: bold; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">', $txt['upgrade_conversion_proceed'], '</span>';
4547
4548
	// Continue please!
4549
	$upcontext['continue'] = $support_js ? 2 : 1;
4550
4551
	// If javascript allows we want to do this using XML.
4552 View Code Duplication
	if ($support_js)
4553
	{
4554
		echo '
4555
					<script>
4556
						var lastTable = ', $upcontext['cur_table_num'], ';
4557
						function getNextTables()
4558
						{
4559
							getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onConversionUpdate);
4560
						}
4561
4562
						// Got an update!
4563
						function onConversionUpdate(oXMLDoc)
4564
						{
4565
							var sCurrentTableName = "";
4566
							var iTableNum = 0;
4567
							var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4568
							for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4569
								sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4570
							iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4571
4572
							// Update the page.
4573
							setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4574
							setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4575
							lastTable = iTableNum;
4576
							updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4577
4578
		// If debug flood the screen.
4579
		if ($is_debug)
4580
			echo '
4581
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4582
4583
						if (document.getElementById(\'debug_section\').scrollHeight)
4584
							document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4585
4586
		echo '
4587
						// Get the next update...
4588
						if (iTableNum == ', $upcontext['table_count'], ')
4589
						{
4590
							document.getElementById(\'commess\').style.display = "";
4591
							if (document.getElementById(\'indexmsg\') != null) {
4592
								document.getElementById(\'indexmsg\').style.display = "";
4593
							}
4594
							document.getElementById(\'current_tab_div\').style.display = "none";
4595
							document.getElementById(\'contbutt\').disabled = 0;
4596
							document.getElementById(\'utf8_done\').value = 1;
4597
						}
4598
						else
4599
							getNextTables();
4600
					}
4601
					getNextTables();
4602
				//# sourceURL=dynamicScript-conv.js
4603
				</script>';
4604
	}
4605
}
4606
4607
function template_convert_xml()
4608
{
4609
	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...
4610
4611
	echo '
4612
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4613
}
4614
4615
// Template for the database backup tool/
4616
function template_serialize_json()
4617
{
4618
	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...
4619
4620
	echo '
4621
				<h3>', $txt['upgrade_convert_datajson'], '</h3>
4622
				<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4623
					<input type="hidden" name="json_done" id="json_done" value="0">
4624
					<strong>', $txt['upgrade_completed'], ' <span id="tab_done">', $upcontext['cur_table_num'], '</span> ', $txt['upgrade_outof'], ' ', $upcontext['table_count'], ' ', $txt['upgrade_tables'], '</strong>
4625
					<div id="debug_section" style="height: ', ($is_debug ? '115' : '12') , 'px; overflow: auto;">
4626
						<span id="debuginfo"></span>
4627
					</div>';
4628
4629
	// Dont any tables so far?
4630 View Code Duplication
	if (!empty($upcontext['previous_tables']))
4631
		foreach ($upcontext['previous_tables'] as $table)
4632
			echo '
4633
					<br>', $txt['upgrade_completed_table'], ' &quot;', $table, '&quot;.';
4634
4635
	echo '
4636
					<h3 id="current_tab_div">', $txt['upgrade_current_table'], ' &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
4637
					<br><span id="commess" style="font-weight: bold; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">', $txt['upgrade_json_completed'], '</span>';
4638
4639
	// Try to make sure substep was reset.
4640
	if ($upcontext['cur_table_num'] == $upcontext['table_count'])
4641
		echo '
4642
					<input type="hidden" name="substep" id="substep" value="0">';
4643
4644
	// Continue please!
4645
	$upcontext['continue'] = $support_js ? 2 : 1;
4646
4647
	// If javascript allows we want to do this using XML.
4648 View Code Duplication
	if ($support_js)
4649
	{
4650
		echo '
4651
					<script>
4652
						var lastTable = ', $upcontext['cur_table_num'], ';
4653
						function getNextTables()
4654
						{
4655
							getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4656
						}
4657
4658
						// Got an update!
4659
						function onBackupUpdate(oXMLDoc)
4660
						{
4661
							var sCurrentTableName = "";
4662
							var iTableNum = 0;
4663
							var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4664
							for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4665
								sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4666
							iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4667
4668
							// Update the page.
4669
							setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4670
							setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4671
							lastTable = iTableNum;
4672
							updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4673
4674
		// If debug flood the screen.
4675
		if ($is_debug)
4676
			echo '
4677
							setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>', $txt['upgrade_completed_table'], ' &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4678
4679
							if (document.getElementById(\'debug_section\').scrollHeight)
4680
								document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4681
4682
		echo '
4683
							// Get the next update...
4684
							if (iTableNum == ', $upcontext['table_count'], ')
4685
							{
4686
								document.getElementById(\'commess\').style.display = "";
4687
								document.getElementById(\'current_tab_div\').style.display = "none";
4688
								document.getElementById(\'contbutt\').disabled = 0;
4689
								document.getElementById(\'json_done\').value = 1;
4690
							}
4691
							else
4692
								getNextTables();
4693
						}
4694
						getNextTables();
4695
					//# sourceURL=dynamicScript-json.js
4696
					</script>';
4697
	}
4698
}
4699
4700
function template_serialize_json_xml()
4701
{
4702
	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...
4703
4704
	echo '
4705
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4706
}
4707
4708
function template_upgrade_complete()
4709
{
4710
	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...
4711
4712
	echo '
4713
				<h3>', $txt['upgrade_done'], ' <a href="', $boardurl, '/index.php">', $txt['upgrade_done2'], '</a>.  ', $txt['upgrade_done3'], '</h3>
4714
				<form action="', $boardurl, '/index.php">';
4715
4716
	if (!empty($upcontext['can_delete_script']))
4717
		echo '
4718
					<label for="delete_self"><input type="checkbox" id="delete_self" onclick="doTheDelete(this);"> ', $txt['upgrade_delete_now'], '</label> <em>', $txt['upgrade_delete_server'], '</em>
4719
					<script>
4720
						function doTheDelete(theCheck)
4721
						{
4722
							var theImage = document.getElementById ? document.getElementById("delete_upgrader") : document.all.delete_upgrader;
4723
							theImage.src = "', $upgradeurl, '?delete=1&ts_" + (new Date().getTime());
4724
							theCheck.disabled = true;
4725
						}
4726
					</script>
4727
					<img src="', $settings['default_theme_url'], '/images/blank.png" alt="" id="delete_upgrader"><br>';
4728
4729
	$active = time() - $upcontext['started'];
4730
	$hours = floor($active / 3600);
4731
	$minutes = intval(($active / 60) % 60);
4732
	$seconds = intval($active % 60);
4733
4734
	if ($is_debug)
4735
	{
4736
		$totalTime = '';
4737
		if ($hours > 0)
4738
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
4739
		if ($minutes > 0)
4740
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
4741
		if ($seconds > 0)
4742
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
4743
	}
4744
4745
	if ($is_debug && !empty($totalTime))
4746
		echo '
4747
					<br> ', $txt['upgrade_completed_time'], ' ', $totalTime, '<br><br>';
4748
4749
	echo '<br>
4750
					', sprintf($txt['upgrade_problems'], 'http://simplemachines.org'), '<br>
4751
					<br>
4752
					', $txt['upgrade_luck'], '<br>
4753
					Simple Machines';
4754
}
4755
4756
/**
4757
 * Convert MySQL (var)char ip col to binary
4758
 *
4759
 * @param string $targetTable The table to perform the operation on
4760
 * @param string $oldCol The old column to gather data from
4761
 * @param string $newCol The new column to put data in
4762
 * @param int $limit The amount of entries to handle at once.
4763
 * @param int $setSize The amount of entries after which to update the database.
4764
 *
4765
 * newCol needs to be a varbinary(16) null able field
4766
 * @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...
4767
 */
4768
function MySQLConvertOldIp($targetTable, $oldCol, $newCol, $limit = 50000, $setSize = 100)
4769
{
4770
	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...
4771
4772
	$current_substep = $_GET['substep'];
4773
4774
	if (empty($_GET['a']))
4775
		$_GET['a'] = 0;
4776
	$step_progress['name'] = 'Converting ips';
4777
	$step_progress['current'] = $_GET['a'];
4778
4779
	// Skip this if we don't have the column
4780
	$request = $smcFunc['db_query']('', '
4781
		SHOW FIELDS
4782
		FROM {db_prefix}{raw:table}
4783
		WHERE Field = {string:name}',
4784
		array(
4785
			'table' => $targetTable,
4786
			'name' => $oldCol,
4787
	));
4788
	if ($smcFunc['db_num_rows']($request) !== 1)
4789
	{
4790
		$smcFunc['db_free_result']($request);
4791
		return;
4792
	}
4793
	$smcFunc['db_free_result']($request);
4794
4795
	//mysql default max length is 1mb https://dev.mysql.com/doc/refman/5.1/en/packet-too-large.html
4796
	$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...
4797
4798
	$is_done = false;
4799
	while (!$is_done)
4800
	{
4801
		// Keep looping at the current step.
4802
		nextSubstep($current_substep);
4803
4804
		$arIp = array();
4805
4806
		$request = $smcFunc['db_query']('', '
4807
			SELECT DISTINCT {raw:old_col}
4808
			FROM {db_prefix}{raw:table_name}
4809
			WHERE {raw:new_col} IS NULL
4810
			LIMIT {int:limit}',
4811
			array(
4812
				'old_col' => $oldCol,
4813
				'new_col' => $newCol,
4814
				'table_name' => $targetTable,
4815
				'empty' => '',
4816
				'limit' => $limit,
4817
		));
4818
		while ($row = $smcFunc['db_fetch_assoc']($request))
4819
			$arIp[] = $row[$oldCol];
4820
		$smcFunc['db_free_result']($request);
4821
4822
		// Special case, null ip could keep us in a loop.
4823
		if (is_null($arIp[0]))
4824
			unset($arIp[0]);
4825
4826
		if (empty($arIp))
4827
			$is_done = true;
4828
4829
		$updates = array();
4830
		$cases = array();
4831
		$count = count($arIp);
4832
		for ($i = 0; $i < $count; $i++)
4833
		{
4834
			$arIp[$i] = trim($arIp[$i]);
4835
4836
			if (empty($arIp[$i]))
4837
				continue;
4838
4839
			$updates['ip' . $i] = $arIp[$i];
4840
			$cases[$arIp[$i]] = 'WHEN ' . $oldCol . ' = {string:ip' . $i . '} THEN {inet:ip' . $i . '}';
4841
4842
			if ($setSize > 0 && $i % $setSize === 0)
4843
			{
4844
				if (count($updates) == 1)
4845
					continue;
4846
4847
				$updates['whereSet'] = array_values($updates);
4848
				$smcFunc['db_query']('', '
4849
					UPDATE {db_prefix}' . $targetTable . '
4850
					SET ' . $newCol . ' = CASE ' .
4851
					implode('
4852
						', $cases) . '
4853
						ELSE NULL
4854
					END
4855
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4856
					$updates
4857
				);
4858
4859
				$updates = array();
4860
				$cases = array();
4861
			}
4862
		}
4863
4864
		// Incase some extras made it through.
4865
		if (!empty($updates))
4866
		{
4867
			if (count($updates) == 1)
4868
			{
4869
				foreach ($updates as $key => $ip)
4870
				{
4871
					$smcFunc['db_query']('', '
4872
						UPDATE {db_prefix}' . $targetTable . '
4873
						SET ' . $newCol . ' = {inet:ip}
4874
						WHERE ' . $oldCol . ' = {string:ip}',
4875
						array(
4876
							'ip' => $ip
4877
					));
4878
				}
4879
			}
4880
			else
4881
			{
4882
				$updates['whereSet'] = array_values($updates);
4883
				$smcFunc['db_query']('', '
4884
					UPDATE {db_prefix}' . $targetTable . '
4885
					SET ' . $newCol . ' = CASE ' .
4886
					implode('
4887
						', $cases) . '
4888
						ELSE NULL
4889
					END
4890
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4891
					$updates
4892
				);
4893
			}
4894
		}
4895
		else
4896
			$is_done = true;
4897
4898
		$_GET['a'] += $limit;
4899
		$step_progress['current'] = $_GET['a'];
4900
	}
4901
4902
	unset($_GET['a']);
4903
}
4904
4905
/**
4906
 * Get the column info. This is basically the same as smf_db_list_columns but we get 1 column, force detail and other checks.
4907
 *
4908
 * @param string $targetTable The table to perform the operation on
4909
 * @param string $column The column we are looking for.
4910
 *
4911
 * @return array Info on the table.
4912
 */
4913
function upgradeGetColumnInfo($targetTable, $column)
4914
{
4915
	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...
4916
4917
	// This should already be here, but be safe.
4918
	db_extend('packages');
4919
4920
	$columns = $smcFunc['db_list_columns']($targetTable, true);
4921
4922
	if (isset($columns[$column]))
4923
		return $columns[$column];
4924
	else
4925
		return null;
4926
}
4927
4928
/**
4929
 * Takes the changes to be made during the upgradeOptions step, grabs all known Settings data from Settings.php, then runs
4930
 * through a process to rebuild onto a brand new Settings template.  This should only be done if detection believes the
4931
 * settings file isn't using any advanced configuration setups in the Settings.php file.  A copy is made as Settings_org.php
4932
 * to preserve all changes prior to migration.
4933
 *
4934
 * @param array $config_vars An array of one or more variables to update
0 ignored issues
show
Bug introduced by
There is no parameter named $config_vars. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
4935
 *
4936
 * @return void We either succesfully update the Settings file, or throw a error here.
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...
4937
 */
4938
function migrateSettingsFile($changes)
4939
{
4940
	global $boarddir, $cachedir;
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...
4941
4942
	// Try to find all of these settings.
4943
	$settingsVars = array(
4944
		'maintenance' => 'int',
4945
		'mtitle' => 'string',
4946
		'mmessage' => 'string',
4947
		'mbname' => 'string',
4948
		'language' => 'string',
4949
		'boardurl' => 'string',
4950
		'webmaster_email' => 'string',
4951
		'cookiename' => 'string',
4952
		'db_type' => 'string',
4953
		'db_server' => 'string_fatal',
4954
		'db_name' => 'string_fatal',
4955
		'db_user' => 'string_fatal',
4956
		'db_passwd' => 'string_fatal',
4957
		'ssi_db_user' => 'string',
4958
		'ssi_db_user' => 'string',
4959
		'db_prefix' => 'string_fatal',
4960
		'db_persist' => 'int',
4961
		'db_error_send' => 'int',
4962
		'db_mb4' => 'null',
4963
		'cache_accelerator' => 'string',
4964
		'cache_enable' => 'int',
4965
		'cache_memcached' => 'string',
4966
		'cachedir' => 'string',
4967
		'image_proxy_enabled' => 'bool',
4968
		'image_proxy_secret' => 'string',
4969
		'image_proxy_maxsize' => 'int',
4970
		'boarddir' => 'string',
4971
		'sourcedir' => 'string',
4972
		'packagesdir' => 'string',
4973
		'tasksdir' => 'string',
4974
	);
4975
4976
	// The Settings file, in an array as if it was handled by updateSettingsFile
4977
	$settingsArray = array(
4978
		'<' . '?' . 'php',
4979
		'',
4980
		'/**',
4981
		' * The settings file contains all of the basic settings that need to be present when a database/cache is not available.',
4982
		' *',
4983
		' * Simple Machines Forum (SMF)',
4984
		' *',
4985
		' * @package SMF',
4986
		' * @author Simple Machines http://www.simplemachines.org',
4987
		' * @copyright ' . date('Y', time()) . ' Simple Machines and individual contributors',
4988
		' * @license http://www.simplemachines.org/about/smf/license.php BSD',
4989
		' *',
4990
		' * @version ' . SMF_VERSION,
4991
		' */',
4992
		'',
4993
		'########## Maintenance ##########',
4994
		'/**',
4995
		' * The maintenance "mode"',
4996
		' * Set to 1 to enable Maintenance Mode, 2 to make the forum untouchable. (you\'ll have to make it 0 again manually!)',
4997
		' * 0 is default and disables maintenance mode.',
4998
		' * @var int 0, 1, 2',
4999
		' * @global int $maintenance',
5000
		' */',
5001
		'$maintenance = 0;',
5002
		'/**',
5003
		' * Title for the Maintenance Mode message.',
5004
		' * @var string',
5005
		' * @global int $mtitle',
5006
		' */',
5007
		'$mtitle = \'Maintenance Mode\';',
5008
		'/**',
5009
		' * Description of why the forum is in maintenance mode.',
5010
		' * @var string',
5011
		' * @global string $mmessage',
5012
		' */',
5013
		'$mmessage = \'Okay faithful users...we\\\'re attempting to restore an older backup of the database...news will be posted once we\\\'re back!\';',
5014
		'',
5015
		'########## Forum Info ##########',
5016
		'/**',
5017
		' * The name of your forum.',
5018
		' * @var string',
5019
		' */',
5020
		'$mbname = \'My Community\';',
5021
		'/**',
5022
		' * The default language file set for the forum.',
5023
		' * @var string',
5024
		' */',
5025
		'$language = \'english\';',
5026
		'/**',
5027
		' * URL to your forum\'s folder. (without the trailing /!)',
5028
		' * @var string',
5029
		' */',
5030
		'$boardurl = \'http://127.0.0.1/smf\';',
5031
		'/**',
5032
		' * Email address to send emails from. (like [email protected].)',
5033
		' * @var string',
5034
		' */',
5035
		'$webmaster_email = \'[email protected]\';',
5036
		'/**',
5037
		' * Name of the cookie to set for authentication.',
5038
		' * @var string',
5039
		' */',
5040
		'$cookiename = \'SMFCookie21\';',
5041
		'',
5042
		'########## Database Info ##########',
5043
		'/**',
5044
		' * The database type',
5045
		' * Default options: mysql, postgresql',
5046
		' * @var string',
5047
		' */',
5048
		'$db_type = \'mysql\';',
5049
		'/**',
5050
		' * The server to connect to (or a Unix socket)',
5051
		' * @var string',
5052
		' */',
5053
		'$db_server = \'localhost\';',
5054
		'/**',
5055
		' * The database name',
5056
		' * @var string',
5057
		' */',
5058
		'$db_name = \'smf\';',
5059
		'/**',
5060
		' * Database username',
5061
		' * @var string',
5062
		' */',
5063
		'$db_user = \'root\';',
5064
		'/**',
5065
		' * Database password',
5066
		' * @var string',
5067
		' */',
5068
		'$db_passwd = \'\';',
5069
		'/**',
5070
		' * Database user for when connecting with SSI',
5071
		' * @var string',
5072
		' */',
5073
		'$ssi_db_user = \'\';',
5074
		'/**',
5075
		' * Database password for when connecting with SSI',
5076
		' * @var string',
5077
		' */',
5078
		'$ssi_db_passwd = \'\';',
5079
		'/**',
5080
		' * A prefix to put in front of your table names.',
5081
		' * This helps to prevent conflicts',
5082
		' * @var string',
5083
		' */',
5084
		'$db_prefix = \'smf_\';',
5085
		'/**',
5086
		' * Use a persistent database connection',
5087
		' * @var int|bool',
5088
		' */',
5089
		'$db_persist = 0;',
5090
		'/**',
5091
		' *',
5092
		' * @var int|bool',
5093
		' */',
5094
		'$db_error_send = 0;',
5095
		'/**',
5096
		' * Override the default behavior of the database layer for mb4 handling',
5097
		' * null keep the default behavior untouched',
5098
		' * @var null|bool',
5099
		' */',
5100
		'$db_mb4 = null;',
5101
		'',
5102
		'########## Cache Info ##########',
5103
		'/**',
5104
		' * Select a cache system. You want to leave this up to the cache area of the admin panel for',
5105
		' * proper detection of apc, memcached, output_cache, smf, or xcache',
5106
		' * (you can add more with a mod).',
5107
		' * @var string',
5108
		' */',
5109
		'$cache_accelerator = \'\';',
5110
		'/**',
5111
		' * The level at which you would like to cache. Between 0 (off) through 3 (cache a lot).',
5112
		' * @var int',
5113
		' */',
5114
		'$cache_enable = 0;',
5115
		'/**',
5116
		' * This is only used for memcache / memcached. Should be a string of \'server:port,server:port\'',
5117
		' * @var array',
5118
		' */',
5119
		'$cache_memcached = \'\';',
5120
		'/**',
5121
		' * This is only for the \'smf\' file cache system. It is the path to the cache directory.',
5122
		' * It is also recommended that you place this in /tmp/ if you are going to use this.',
5123
		' * @var string',
5124
		' */',
5125
		'$cachedir = dirname(__FILE__) . \'/cache\';',
5126
		'',
5127
		'########## Image Proxy ##########',
5128
		'# This is done entirely in Settings.php to avoid loading the DB while serving the images',
5129
		'/**',
5130
		' * Whether the proxy is enabled or not',
5131
		' * @var bool',
5132
		' */',
5133
		'$image_proxy_enabled = true;',
5134
		'',
5135
		'/**',
5136
		' * Secret key to be used by the proxy',
5137
		' * @var string',
5138
		' */',
5139
		'$image_proxy_secret = \'smfisawesome\';',
5140
		'',
5141
		'/**',
5142
		' * Maximum file size (in KB) for indiviudal files',
5143
		' * @var int',
5144
		' */',
5145
		'$image_proxy_maxsize = 5192;',
5146
		'',
5147
		'########## Directories/Files ##########',
5148
		'# Note: These directories do not have to be changed unless you move things.',
5149
		'/**',
5150
		' * The absolute path to the forum\'s folder. (not just \'.\'!)',
5151
		' * @var string',
5152
		' */',
5153
		'$boarddir = dirname(__FILE__);',
5154
		'/**',
5155
		' * Path to the Sources directory.',
5156
		' * @var string',
5157
		' */',
5158
		'$sourcedir = dirname(__FILE__) . \'/Sources\';',
5159
		'/**',
5160
		' * Path to the Packages directory.',
5161
		' * @var string',
5162
		' */',
5163
		'$packagesdir = dirname(__FILE__) . \'/Packages\';',
5164
		'/**',
5165
		' * Path to the tasks directory.',
5166
		' * @var string',
5167
		' */',
5168
		'$tasksdir = $sourcedir . \'/tasks\';',
5169
		'',
5170
		'# Make sure the paths are correct... at least try to fix them.',
5171
		'if (!file_exists($boarddir) && file_exists(dirname(__FILE__) . \'/agreement.txt\'))',
5172
		'	$boarddir = dirname(__FILE__);',
5173
		'if (!file_exists($sourcedir) && file_exists($boarddir . \'/Sources\'))',
5174
		'	$sourcedir = $boarddir . \'/Sources\';',
5175
		'if (!file_exists($cachedir) && file_exists($boarddir . \'/cache\'))',
5176
		'	$cachedir = $boarddir . \'/cache\';',
5177
		'',
5178
		'########## Error-Catching ##########',
5179
		'# Note: You shouldn\'t touch these settings.',
5180
		'if (file_exists((isset($cachedir) ? $cachedir : dirname(__FILE__)) . \'/db_last_error.php\'))',
5181
		'	include((isset($cachedir) ? $cachedir : dirname(__FILE__)) . \'/db_last_error.php\');',
5182
		'',
5183
		'if (!isset($db_last_error))',
5184
		'{',
5185
		'	// File does not exist so lets try to create it',
5186
		'	file_put_contents((isset($cachedir) ? $cachedir : dirname(__FILE__)) . \'/db_last_error.php\', \'<\' . \'?\' . "php\n" . \'$db_last_error = 0;\' . "\n" . \'?\' . \'>\');',
5187
		'	$db_last_error = 0;',
5188
		'}',
5189
		'',
5190
		'?' . '>',
5191
	);
5192
5193
	// Now, find all of the original settings.  Mark those for the "change".
5194
	$original = array();
5195
	foreach ($settingsVars as $setVar => $setType)
5196
	{
5197
		global $$setVar;
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...
5198
5199
		// Find the setting.
5200
		if ($setType == 'string' || $setType == 'string_fatal')
5201
			$original[$setVar] = isset($$setVar) ? '\'' . addslashes($$setVar) . '\'' : (strpos('fatal', $setType) ? null : '\'\'');
5202
		elseif ($setType == 'int' || $setType == 'int_fatal')
5203
			$original[$setVar] = isset($$setVar) ? (int) $$setVar : (strpos('fatal', $setType) ? null : 0);
5204 View Code Duplication
		elseif ($setType == 'bool' || $setType == 'bool_fatal')
5205
			$original[$setVar] = isset($$setVar) && in_array($$setVar, array(1, true)) ? 'true' : (strpos('fatal', $setType) ? null : 'false');
5206 View Code Duplication
		elseif ($setType == 'null' || $setType == 'null_fatal')
5207
			$original[$setVar] = isset($$setVar) && in_array($$setVar, array(1, true)) ? 'true' : (strpos('fatal', $setType) ? null : 'null');
5208
5209
		// Well this isn't good.  Do we fix it or bail?
5210
		if (is_null($original) && $setType != 'null' && strpos('fatal', $setType) > -1)
5211
			return throw_error('The upgrader could not copy a setting (' . $setVar . ') from your Settings file.  Unable to migrate your Settings file to a new version.');
5212
	}
5213
5214
	// Finally, merge the changes with the new ones.
5215
	$config_vars = $original;
5216
	foreach ($changes as $setVar => $value)
5217
	{
5218
		// Nothing needed here.
5219
		if ($setVar != 'upgradeData' && $config_vars[$setVar] == $changes[$setVar])
5220
			continue;
5221
5222
		$config_vars[$setVar] = $value;
5223
	}
5224
5225
	/*
5226
		It would be nice to call updateSettingsFile and be done with this. However the function doesn't support passing in the entire file. We also want to backup with a different name, just incase.
5227
	*/
5228
5229
	// When was Settings.php last changed?
5230
	$last_settings_change = filemtime($boarddir . '/Settings.php');
5231
5232
	// remove any /r's that made there way in here
5233 View Code Duplication
	foreach ($settingsArray as $k => $dummy)
5234
		$settingsArray[$k] = strtr($dummy, array("\r" => '')) . "\n";
5235
5236
	// go line by line and see whats changing
5237 View Code Duplication
	for ($i = 0, $n = count($settingsArray); $i < $n; $i++)
5238
	{
5239
		// Don't trim or bother with it if it's not a variable.
5240
		if (substr($settingsArray[$i], 0, 1) != '$')
5241
			continue;
5242
5243
		$settingsArray[$i] = trim($settingsArray[$i]) . "\n";
5244
5245
		// Look through the variables to set....
5246
		foreach ($config_vars as $var => $val)
0 ignored issues
show
Bug introduced by
The expression $config_vars of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

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

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

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

Loading history...
5247
		{
5248
			// be sure someone is not updating db_last_error this with a group
5249
			if ($var === 'db_last_error')
5250
				unset($config_vars[$var]);
5251
			elseif (strncasecmp($settingsArray[$i], '$' . $var, 1 + strlen($var)) == 0)
5252
			{
5253
				$comment = strstr(substr($settingsArray[$i], strpos($settingsArray[$i], ';')), '#');
5254
				$settingsArray[$i] = '$' . $var . ' = ' . $val . ';' . ($comment == '' ? '' : "\t\t" . rtrim($comment)) . "\n";
5255
5256
				// This one's been 'used', so to speak.
5257
				unset($config_vars[$var]);
5258
			}
5259
		}
5260
5261
		// End of the file ... maybe
5262
		if (substr(trim($settingsArray[$i]), 0, 2) == '?' . '>')
5263
			$end = $i;
5264
	}
5265
5266
	// This should never happen, but apparently it is happening.
5267
	if (empty($end) || $end < 10)
5268
		$end = count($settingsArray) - 1;
5269
5270
	// Still more variables to go?  Then lets add them at the end.
5271 View Code Duplication
	if (!empty($config_vars))
5272
	{
5273
		if (trim($settingsArray[$end]) == '?' . '>')
5274
			$settingsArray[$end++] = '';
5275
		else
5276
			$end++;
5277
5278
		// Add in any newly defined vars that were passed
5279
		foreach ($config_vars as $var => $val)
5280
			$settingsArray[$end++] = '$' . $var . ' = ' . $val . ';' . "\n";
5281
5282
		$settingsArray[$end] = '?' . '>';
5283
	}
5284
	else
5285
		$settingsArray[$end] = trim($settingsArray[$end]);
5286
5287
	// Sanity error checking: the file needs to be at least 12 lines.
5288
	if (count($settingsArray) < 12)
5289
		return throw_error('The upgrader could not process your Settings file for updates.  Unable to migrate your Settings file to a new version.');
5290
5291
	// Try to avoid a few pitfalls:
5292
	//  - like a possible race condition,
5293
	//  - or a failure to write at low diskspace
5294
	//
5295
	// Check before you act: if cache is enabled, we can do a simple write test
5296
	// to validate that we even write things on this filesystem.
5297 View Code Duplication
	if ((empty($cachedir) || !file_exists($cachedir)) && file_exists($boarddir . '/cache'))
5298
		$cachedir = $boarddir . '/cache';
5299
5300
	$test_fp = @fopen($cachedir . '/settings_update.tmp', "w+");
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal w+ 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...
5301 View Code Duplication
	if ($test_fp)
5302
	{
5303
		fclose($test_fp);
5304
		$written_bytes = file_put_contents($cachedir . '/settings_update.tmp', 'test', LOCK_EX);
5305
		@unlink($cachedir . '/settings_update.tmp');
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...
5306
5307
		// Oops. Low disk space, perhaps. Don't mess with Settings.php then.
5308
		// No means no. :P
5309
		if ($written_bytes !== 4)
5310
			return throw_error('The upgrader could not write a test file, perhaps not enough storage?  Unable to migrate your Settings file to a new version.');
5311
	}
5312
5313
	// Protect me from what I want! :P
5314
	clearstatcache();
5315 View Code Duplication
	if (filemtime($boarddir . '/Settings.php') === $last_settings_change)
5316
	{
5317
		// save the old before we do anything
5318
		$settings_backup_fail = !@is_writable($boarddir . '/Settings_org.php') || !@copy($boarddir . '/Settings.php', $boarddir . '/Settings_org.php');
5319
		$settings_backup_fail = !$settings_backup_fail ? (!file_exists($boarddir . '/Settings_org.php') || filesize($boarddir . '/Settings_org.php') === 0) : $settings_backup_fail;
5320
5321
		// write out the new
5322
		$write_settings = implode('', $settingsArray);
5323
		$written_bytes = file_put_contents($boarddir . '/Settings.php', $write_settings, LOCK_EX);
0 ignored issues
show
Security File Manipulation introduced by
$write_settings can contain request data and is used in file manipulation context(s) leading to a potential security vulnerability.

3 paths for user data to reach this point

  1. Path: Read from $_GET, and $changes is assigned in other/upgrade.php on line 1186
  1. Read from $_GET, and $changes is assigned
    in other/upgrade.php on line 1186
  2. $changes is assigned
    in other/upgrade.php on line 1261
  3. $changes is passed to migrateSettingsFile()
    in other/upgrade.php on line 1263
  4. $value is assigned
    in other/upgrade.php on line 5216
  5. $config_vars is assigned
    in other/upgrade.php on line 5222
  6. $var is assigned
    in other/upgrade.php on line 5246
  7. $settingsArray is assigned
    in other/upgrade.php on line 5254
  8. $settingsArray is assigned
    in other/upgrade.php on line 5282
  9. $settingsArray is passed through implode(), and $write_settings is assigned
    in other/upgrade.php on line 5322
  2. Path: Read from $_POST, and $_POST['maintitle'] is escaped by addslashes() for sql, xpath context(s), and $changes is assigned in other/upgrade.php on line 1196
  1. Read from $_POST, and $_POST['maintitle'] is escaped by addslashes() for sql, xpath context(s), and $changes is assigned
    in other/upgrade.php on line 1196
  2. $changes is assigned
    in other/upgrade.php on line 1197
  3. $changes is assigned
    in other/upgrade.php on line 1261
  4. $changes is passed to migrateSettingsFile()
    in other/upgrade.php on line 1263
  5. $value is assigned
    in other/upgrade.php on line 5216
  6. $config_vars is assigned
    in other/upgrade.php on line 5222
  7. $var is assigned
    in other/upgrade.php on line 5246
  8. $settingsArray is assigned
    in other/upgrade.php on line 5254
  9. $settingsArray is assigned
    in other/upgrade.php on line 5282
  10. $settingsArray is passed through implode(), and $write_settings is assigned
    in other/upgrade.php on line 5322
  3. Path: Read from $_POST, and $_POST['mainmessage'] is escaped by addslashes() for sql, xpath context(s), and $changes is assigned in other/upgrade.php on line 1197
  1. Read from $_POST, and $_POST['mainmessage'] is escaped by addslashes() for sql, xpath context(s), and $changes is assigned
    in other/upgrade.php on line 1197
  2. $changes is assigned
    in other/upgrade.php on line 1261
  3. $changes is passed to migrateSettingsFile()
    in other/upgrade.php on line 1263
  4. $value is assigned
    in other/upgrade.php on line 5216
  5. $config_vars is assigned
    in other/upgrade.php on line 5222
  6. $var is assigned
    in other/upgrade.php on line 5246
  7. $settingsArray is assigned
    in other/upgrade.php on line 5254
  8. $settingsArray is assigned
    in other/upgrade.php on line 5282
  9. $settingsArray is passed through implode(), and $write_settings is assigned
    in other/upgrade.php on line 5322

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...
5324
5325
		// survey says ...
5326
		if ($written_bytes !== strlen($write_settings) && !$settings_backup_fail)
5327
		{
5328
			if (file_exists($boarddir . '/Settings_bak.php'))
5329
				@copy($boarddir . '/Settings_bak.php', $boarddir . '/Settings.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...
5330
5331
			return throw_error('The upgrader detected a bad Settings file and reverted the changes.  Unable to migrate your Settings file to a new version.');
5332
		}
5333
	}
5334
5335
	// Even though on normal installations the filemtime should prevent this being used by the installer incorrectly
5336
	// it seems that there are times it might not. So let's MAKE it dump the cache.
5337
	if (function_exists('opcache_invalidate'))
5338
		opcache_invalidate($boarddir . '/Settings.php', true);
5339
}
5340
5341
/**
5342
 * Determine if we should auto select the migrate Settings file.  This is determined by a variety of missing settings.
5343
 * Prior to checking these settings, we look for advanced setups such as integrations or if variables have been moved
5344
 * to another file.  If these are detected, we abort.
5345
 *
5346
 * @param array $config_vars An array of one or more variables to update
5347
 *
0 ignored issues
show
Bug introduced by
There is no parameter named $config_vars. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
5348
 * @return void We either succesfully update the Settings file, or throw a error here.
5349
 */
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean?

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...
5350
5351
function detectSettingsFileMigrationNeeded()
5352
{
5353
	global $boarddir, $packagesdir, $tasksdir, $db_server, $db_type, $image_proxy_enabled, $db_show_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...
5354
5355
	// We should not migrate if db_show_debug is in there, some dev stuff going on here.
5356
	if (isset($db_show_debug))
5357
		return false;
5358
5359
	$file_contents = file_get_contents($boarddir . '/Settings.php');
5360
5361
	// Is there a include statement somewhere in there? Some advanced handling of the variables elsewhere?
5362
	// Try our best to stay away from the cachedir match.
5363
	if (preg_match('~\sinclude\((?:(?!\(isset\(\$cachedir))~im', $file_contents))
5364
		return false;
5365
5366
	// If we find a mention of $GLOBALS, there may be a integration going on.
5367
	if (preg_match('~\$GLOBALS\[~im', $file_contents))
5368
		return false;
5369
5370
	// If these are not set, it makes us a canidate to migrate.
5371
	if (!isset($packagesdir, $tasksdir, $db_server, $db_type, $image_proxy_enabled))
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return !isset($packagesd... $image_proxy_enabled);.
Loading history...
5372
		return true;
5373
5374
	return false;
5375
}
5376
5377
?>