Completed
Push — release-2.1 ( c58202...ffbace )
by Jeremy
16s
created

upgrade.php ➔ template_upgrade_above()   C

Complexity

Conditions 14
Paths 4

Size

Total Lines 105

Duplication

Lines 3
Ratio 2.86 %

Importance

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

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

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

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

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

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

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

    public function myFunction() {
        // Do something
    }
}
Loading history...
235
236
	$upcontext['upgrade_status'] = json_decode(base64_decode($_GET['data']), true);
237
	$upcontext['current_step'] = $upcontext['upgrade_status']['curstep'];
238
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
239
	$upcontext['rid'] = $upcontext['upgrade_status']['rid'];
240
	$support_js = $upcontext['upgrade_status']['js'];
241
242
	// Only set this if the upgrader status says so.
243
	if (empty($is_debug))
244
		$is_debug = $upcontext['upgrade_status']['debug'];
245
246
	// Load the language.
247
	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-350) is considered.

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

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

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

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

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

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

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

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

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

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

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

    public function myFunction() {
        // Do something
    }
}
Loading history...
1050
1051
	$upcontext['sub_template'] = 'upgrade_options';
1052
	$upcontext['page_title'] = $txt['upgrade_options'];
1053
1054
	db_extend('packages');
1055
	$upcontext['karma_installed'] = array('good' => false, 'bad' => false);
1056
	$member_columns = $smcFunc['db_list_columns']('{db_prefix}members');
1057
1058
	$upcontext['karma_installed']['good'] = in_array('karma_good', $member_columns);
1059
	$upcontext['karma_installed']['bad'] = in_array('karma_bad', $member_columns);
1060
1061
	unset($member_columns);
1062
1063
	// If we've not submitted then we're done.
1064
	if (empty($_POST['upcont']))
1065
		return false;
1066
1067
	// Firstly, if they're enabling SM stat collection just do it.
1068
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']))
1069
	{
1070
		$upcontext['allow_sm_stats'] = true;
1071
1072
		// Don't register if we still have a key.
1073
		if (empty($modSettings['sm_stats_key']))
1074
		{
1075
			// Attempt to register the site etc.
1076
			$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...
1077 View Code Duplication
			if ($fp)
1078
			{
1079
				$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
1080
				$out .= 'Host: www.simplemachines.org' . "\r\n";
1081
				$out .= 'Connection: Close' . "\r\n\r\n";
1082
				fwrite($fp, $out);
1083
1084
				$return_data = '';
1085
				while (!feof($fp))
1086
					$return_data .= fgets($fp, 128);
1087
1088
				fclose($fp);
1089
1090
				// Get the unique site ID.
1091
				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...
1092
1093
				if (!empty($ID[1]))
1094
					$smcFunc['db_insert']('replace',
1095
						$db_prefix . 'settings',
1096
						array('variable' => 'string', 'value' => 'string'),
1097
						array(
1098
							array('sm_stats_key', $ID[1]),
1099
							array('enable_sm_stats', 1),
1100
						),
1101
						array('variable')
1102
					);
1103
			}
1104
		}
1105
		else
1106
		{
1107
			$smcFunc['db_insert']('replace',
1108
				$db_prefix . 'settings',
1109
				array('variable' => 'string', 'value' => 'string'),
1110
				array('enable_sm_stats', 1),
1111
				array('variable')
1112
			);
1113
		}
1114
	}
1115
	// Don't remove stat collection unless we unchecked the box for real, not from the loop.
1116 View Code Duplication
	elseif (empty($_POST['stats']) && empty($upcontext['allow_sm_stats']))
1117
		$smcFunc['db_query']('', '
1118
			DELETE FROM {db_prefix}settings
1119
			WHERE variable = {string:enable_sm_stats}',
1120
			array(
1121
				'enable_sm_stats' => 'enable_sm_stats',
1122
				'db_error_skip' => true,
1123
			)
1124
		);
1125
1126
	// Deleting old karma stuff?
1127
	if (!empty($_POST['delete_karma']))
1128
	{
1129
		// Delete old settings vars.
1130
		$smcFunc['db_query']('', '
1131
			DELETE FROM {db_prefix}settings
1132
			WHERE variable IN ({array_string:karma_vars})',
1133
			array(
1134
				'karma_vars' => array('karmaMode', 'karmaTimeRestrictAdmins', 'karmaWaitTime', 'karmaMinPosts', 'karmaLabel', 'karmaSmiteLabel', 'karmaApplaudLabel'),
1135
			)
1136
		);
1137
1138
		// Cleaning up old karma member settings.
1139
		if ($upcontext['karma_installed']['good'])
1140
			$smcFunc['db_query']('', '
1141
				ALTER TABLE {db_prefix}members
1142
				DROP karma_good',
1143
				array()
1144
			);
1145
1146
		// Does karma bad was enable?
1147
		if ($upcontext['karma_installed']['bad'])
1148
			$smcFunc['db_query']('', '
1149
				ALTER TABLE {db_prefix}members
1150
				DROP karma_bad',
1151
				array()
1152
			);
1153
1154
		// Cleaning up old karma permissions.
1155
		$smcFunc['db_query']('', '
1156
			DELETE FROM {db_prefix}permissions
1157
			WHERE permission = {string:karma_vars}',
1158
			array(
1159
				'karma_vars' => 'karma_edit',
1160
			)
1161
		);
1162
		// Cleaning up old log_karma table
1163
		$smcFunc['db_query']('', '
1164
			DROP TABLE IF EXISTS {db_prefix}log_karma',
1165
			array()
1166
		);
1167
	}
1168
1169
	// Emptying the error log?
1170
	if (!empty($_POST['empty_error']))
1171
		$smcFunc['db_query']('truncate_table', '
1172
			TRUNCATE {db_prefix}log_errors',
1173
			array(
1174
			)
1175
		);
1176
1177
	$changes = array();
1178
1179
	// Add proxy settings.
1180
	if (!isset($GLOBALS['image_proxy_maxsize']))
1181
		$changes += array(
1182
			'image_proxy_secret' => '\'' . substr(sha1(mt_rand()), 0, 20) . '\'',
1183
			'image_proxy_maxsize' => 5190,
1184
			'image_proxy_enabled' => 0,
1185
		);
1186
1187
	// If $boardurl reflects https, set force_ssl
1188
	if (!function_exists('cache_put_data'))
1189
		require_once($sourcedir . '/Load.php');
1190
	if (stripos($boardurl, 'https://') !== false)
1191
		updateSettings(array('force_ssl' => '1'));
1192
1193
	// If we're overriding the language follow it through.
1194
	if (isset($_GET['lang']) && file_exists($modSettings['theme_dir'] . '/languages/index.' . $_GET['lang'] . '.php'))
1195
		$changes['language'] = '\'' . $_GET['lang'] . '\'';
1196
1197
	if (!empty($_POST['maint']))
1198
	{
1199
		$changes['maintenance'] = '2';
1200
		// Remember what it was...
1201
		$upcontext['user']['main'] = $maintenance;
1202
1203
		if (!empty($_POST['maintitle']))
1204
		{
1205
			$changes['mtitle'] = '\'' . addslashes($_POST['maintitle']) . '\'';
1206
			$changes['mmessage'] = '\'' . addslashes($_POST['mainmessage']) . '\'';
1207
		}
1208
		else
1209
		{
1210
			$changes['mtitle'] = '\'Upgrading the forum...\'';
1211
			$changes['mmessage'] = '\'Don\\\'t worry, we will be back shortly with an updated forum.  It will only be a minute ;).\'';
1212
		}
1213
	}
1214
1215
	if ($command_line)
1216
		echo ' * Updating Settings.php...';
1217
1218
	// Fix some old paths.
1219
	if (substr($boarddir, 0, 1) == '.')
1220
		$changes['boarddir'] = '\'' . fixRelativePath($boarddir) . '\'';
1221
1222
	if (substr($sourcedir, 0, 1) == '.')
1223
		$changes['sourcedir'] = '\'' . fixRelativePath($sourcedir) . '\'';
1224
1225
	if (empty($cachedir) || substr($cachedir, 0, 1) == '.')
1226
		$changes['cachedir'] = '\'' . fixRelativePath($boarddir) . '/cache\'';
1227
1228
	// If they have a "host:port" setup for the host, split that into separate values
1229
	// You should never have a : in the hostname if you're not on MySQL, but better safe than sorry
1230
	if (strpos($db_server, ':') !== false && $db_type == 'mysql')
1231
	{
1232
		list ($db_server, $db_port) = explode(':', $db_server);
1233
1234
		$changes['db_server'] = '\'' . $db_server . '\'';
1235
1236
		// Only set this if we're not using the default port
1237
		if ($db_port != ini_get('mysqli.default_port'))
1238
			$changes['db_port'] = (int) $db_port;
1239
	}
1240
	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...
1241
	{
1242
		// If db_port is set and is the same as the default, set it to ''
1243
		if ($db_type == 'mysql')
1244
		{
1245
			if ($db_port == ini_get('mysqli.default_port'))
1246
				$changes['db_port'] = '\'\'';
1247
			elseif ($db_type == 'postgresql' && $db_port == 5432)
1248
				$changes['db_port'] = '\'\'';
1249
		}
1250
	}
1251
1252
	// Maybe we haven't had this option yet?
1253
	if (empty($packagesdir))
1254
		$changes['packagesdir'] = '\'' . fixRelativePath($boarddir) . '/Packages\'';
1255
1256
	// Add support for $tasksdir var.
1257
	if (empty($tasksdir))
1258
		$changes['tasksdir'] = '\'' . fixRelativePath($sourcedir) . '/tasks\'';
1259
1260
	// Make sure we fix the language as well.
1261
	if (stristr($language, '-utf8'))
1262
		$changes['language'] = '\'' . str_ireplace('-utf8', '', $language) . '\'';
1263
1264
	// @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...
1265
1266
	// Update Settings.php with the new settings.
1267
	require_once($sourcedir . '/Subs-Admin.php');
1268
	updateSettingsFile($changes);
1269
1270
	// Tell Settings.php to store db_last_error.php in the cache
1271
	move_db_last_error_to_cachedir();
1272
1273
	if ($command_line)
1274
		echo ' Successful.' . "\n";
1275
1276
	// Are we doing debug?
1277
	if (isset($_POST['debug']))
1278
	{
1279
		$upcontext['upgrade_status']['debug'] = true;
1280
		$is_debug = true;
1281
	}
1282
1283
	// If we're not backing up then jump one.
1284
	if (empty($_POST['backup']))
1285
		$upcontext['current_step']++;
1286
1287
	// If we've got here then let's proceed to the next step!
1288
	return true;
1289
}
1290
1291
// Backup the database - why not...
1292
function BackupDatabase()
1293
{
1294
	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...
1295
1296
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1297
	$upcontext['page_title'] = $txt['backup_database'];
1298
1299
	// Done it already - js wise?
1300
	if (!empty($_POST['backup_done']))
1301
		return true;
1302
1303
	// Some useful stuff here.
1304
	db_extend();
1305
1306
	// Might need this as well
1307
	db_extend('packages');
1308
1309
	// Get all the table names.
1310
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1311
	$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...
1312
	$tables = $smcFunc['db_list_tables']($db, $filter);
1313
1314
	$table_names = array();
1315
	foreach ($tables as $table)
1316
		if (substr($table, 0, 7) !== 'backup_')
1317
			$table_names[] = $table;
1318
1319
	$upcontext['table_count'] = count($table_names);
1320
	$upcontext['cur_table_num'] = $_GET['substep'];
1321
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1322
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1323
	// For non-java auto submit...
1324
	$file_steps = $upcontext['table_count'];
1325
1326
	// What ones have we already done?
1327 View Code Duplication
	foreach ($table_names as $id => $table)
1328
		if ($id < $_GET['substep'])
1329
			$upcontext['previous_tables'][] = $table;
1330
1331
	if ($command_line)
1332
		echo 'Backing Up Tables.';
1333
1334
	// If we don't support javascript we backup here.
1335
	if (!$support_js || isset($_GET['xml']))
1336
	{
1337
		// Backup each table!
1338
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1339
		{
1340
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1341
			$upcontext['cur_table_num'] = $substep + 1;
1342
1343
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1344
1345
			// Do we need to pause?
1346
			nextSubstep($substep);
1347
1348
			backupTable($table_names[$substep]);
1349
1350
			// If this is XML to keep it nice for the user do one table at a time anyway!
1351
			if (isset($_GET['xml']))
1352
				return upgradeExit();
1353
		}
1354
1355
		if ($command_line)
1356
		{
1357
			echo "\n" . ' Successful.\'' . "\n";
1358
			flush();
1359
		}
1360
		$upcontext['step_progress'] = 100;
1361
1362
		$_GET['substep'] = 0;
1363
		// Make sure we move on!
1364
		return true;
1365
	}
1366
1367
	// Either way next place to post will be database changes!
1368
	$_GET['substep'] = 0;
1369
	return false;
1370
}
1371
1372
// Backup one table...
1373
function backupTable($table)
1374
{
1375
	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...
1376
1377
	if ($command_line)
1378
	{
1379
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1380
		flush();
1381
	}
1382
1383
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1384
1385
	if ($command_line)
1386
		echo ' done.';
1387
}
1388
1389
// Step 2: Everything.
1390
function DatabaseChanges()
1391
{
1392
	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...
1393
	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...
1394
1395
	// Have we just completed this?
1396
	if (!empty($_POST['database_done']))
1397
		return true;
1398
1399
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1400
	$upcontext['page_title'] = $txt['database_changes'];
1401
1402
	// All possible files.
1403
	// Name, < version, insert_on_complete
1404
	$files = array(
1405
		array('upgrade_1-0.sql', '1.1', '1.1 RC0'),
1406
		array('upgrade_1-1.sql', '2.0', '2.0 a'),
1407
		array('upgrade_2-0_' . $db_type . '.sql', '2.1', '2.1 dev0'),
1408
		array('upgrade_2-1_' . $db_type . '.sql', '3.0', SMF_VERSION),
1409
	);
1410
1411
	// How many files are there in total?
1412
	if (isset($_GET['filecount']))
1413
		$upcontext['file_count'] = (int) $_GET['filecount'];
1414
	else
1415
	{
1416
		$upcontext['file_count'] = 0;
1417
		foreach ($files as $file)
1418
		{
1419
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1420
				$upcontext['file_count']++;
1421
		}
1422
	}
1423
1424
	// Do each file!
1425
	$did_not_do = count($files) - $upcontext['file_count'];
1426
	$upcontext['step_progress'] = 0;
1427
	$upcontext['cur_file_num'] = 0;
1428
	foreach ($files as $file)
1429
	{
1430
		if ($did_not_do)
1431
			$did_not_do--;
1432
		else
1433
		{
1434
			$upcontext['cur_file_num']++;
1435
			$upcontext['cur_file_name'] = $file[0];
1436
			// Do we actually need to do this still?
1437
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1438
			{
1439
				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1440
				if ($nextFile)
1441
				{
1442
					// Only update the version of this if complete.
1443
					$smcFunc['db_insert']('replace',
1444
						$db_prefix . 'settings',
1445
						array('variable' => 'string', 'value' => 'string'),
1446
						array('smfVersion', $file[2]),
1447
						array('variable')
1448
					);
1449
1450
					$modSettings['smfVersion'] = $file[2];
1451
				}
1452
1453
				// If this is XML we only do this stuff once.
1454
				if (isset($_GET['xml']))
1455
				{
1456
					// Flag to move on to the next.
1457
					$upcontext['completed_step'] = true;
1458
					// Did we complete the whole file?
1459
					if ($nextFile)
1460
						$upcontext['current_debug_item_num'] = -1;
1461
					return upgradeExit();
1462
				}
1463
				elseif ($support_js)
1464
					break;
1465
			}
1466
			// Set the progress bar to be right as if we had - even if we hadn't...
1467
			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1468
		}
1469
	}
1470
1471
	$_GET['substep'] = 0;
1472
	// So the template knows we're done.
1473
	if (!$support_js)
1474
	{
1475
		$upcontext['changes_complete'] = true;
1476
1477
		return true;
1478
	}
1479
	return false;
1480
}
1481
1482
1483
// Delete the damn thing!
1484
function DeleteUpgrade()
1485
{
1486
	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...
1487
	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...
1488
1489
	// Now it's nice to have some of the basic SMF source files.
1490
	if (!isset($_GET['ssi']) && !$command_line)
1491
		redirectLocation('&ssi=1');
1492
1493
	$upcontext['sub_template'] = 'upgrade_complete';
1494
	$upcontext['page_title'] = $txt['upgrade_complete'];
1495
1496
	$endl = $command_line ? "\n" : '<br>' . "\n";
1497
1498
	$changes = array(
1499
		'language' => '\'' . (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language) . '\'',
1500
		'db_error_send' => '1',
1501
		'upgradeData' => '\'\'',
1502
	);
1503
1504
	// Are we in maintenance mode?
1505
	if (isset($upcontext['user']['main']))
1506
	{
1507
		if ($command_line)
1508
			echo ' * ';
1509
		$upcontext['removed_maintenance'] = true;
1510
		$changes['maintenance'] = $upcontext['user']['main'];
1511
	}
1512
	// Otherwise if somehow we are in 2 let's go to 1.
1513
	elseif (!empty($maintenance) && $maintenance == 2)
1514
		$changes['maintenance'] = 1;
1515
1516
	// Wipe this out...
1517
	$upcontext['user'] = array();
1518
1519
	require_once($sourcedir . '/Subs-Admin.php');
1520
	updateSettingsFile($changes);
1521
1522
	// Clean any old cache files away.
1523
	upgrade_clean_cache();
1524
1525
	// Can we delete the file?
1526
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1527
1528
	// Now is the perfect time to fetch the SM files.
1529
	if ($command_line)
1530
		cli_scheduled_fetchSMfiles();
1531
	else
1532
	{
1533
		require_once($sourcedir . '/ScheduledTasks.php');
1534
		$forum_version = SMF_VERSION; // The variable is usually defined in index.php so lets just use the constant to do it for us.
1535
		scheduled_fetchSMfiles(); // Now go get those files!
1536
		// This is needed in case someone invokes the upgrader using https when upgrading an http forum
1537 View Code Duplication
		if (httpsOn())
1538
			$settings['default_theme_url'] = strtr($settings['default_theme_url'], array('http://' => 'https://'));
1539
	}
1540
1541
	// Log what we've done.
1542
	if (empty($user_info['id']))
1543
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
1544
1545
	// Log the action manually, so CLI still works.
1546
	$smcFunc['db_insert']('',
1547
		'{db_prefix}log_actions',
1548
		array(
1549
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'inet', 'action' => 'string',
1550
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
1551
		),
1552
		array(
1553
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
1554
			0, 0, 0, json_encode(array('version' => $forum_version, 'member' => $user_info['id'])),
1555
		),
1556
		array('id_action')
1557
	);
1558
	$user_info['id'] = 0;
1559
1560
	// Save the current database version.
1561
	$server_version = $smcFunc['db_server_info']();
1562 View Code Duplication
	if ($db_type == 'mysql' && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51')))
1563
		updateSettings(array('db_mysql_group_by_fix' => '1'));
1564
1565
	if ($command_line)
1566
	{
1567
		echo $endl;
1568
		echo 'Upgrade Complete!', $endl;
1569
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
1570
		exit;
1571
	}
1572
1573
	// Make sure it says we're done.
1574
	$upcontext['overall_percent'] = 100;
1575
	if (isset($upcontext['step_progress']))
1576
		unset($upcontext['step_progress']);
1577
1578
	$_GET['substep'] = 0;
1579
	return false;
1580
}
1581
1582
// Just like the built in one, but setup for CLI to not use themes.
1583
function cli_scheduled_fetchSMfiles()
1584
{
1585
	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...
1586
1587
	if (empty($modSettings['time_format']))
1588
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1589
1590
	// What files do we want to get
1591
	$request = $smcFunc['db_query']('', '
1592
		SELECT id_file, filename, path, parameters
1593
		FROM {db_prefix}admin_info_files',
1594
		array(
1595
		)
1596
	);
1597
1598
	$js_files = array();
1599 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
1600
	{
1601
		$js_files[$row['id_file']] = array(
1602
			'filename' => $row['filename'],
1603
			'path' => $row['path'],
1604
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode($forum_version)),
1605
		);
1606
	}
1607
	$smcFunc['db_free_result']($request);
1608
1609
	// We're gonna need fetch_web_data() to pull this off.
1610
	require_once($sourcedir . '/Subs.php');
1611
1612
	foreach ($js_files as $ID_FILE => $file)
1613
	{
1614
		// Create the url
1615
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'https://www.simplemachines.org' : '';
1616
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1617
1618
		// Get the file
1619
		$file_data = fetch_web_data($url);
1620
1621
		// If we got an error - give up - the site might be down.
1622
		if ($file_data === false)
1623
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
1624
1625
		// Save the file to the database.
1626
		$smcFunc['db_query']('substring', '
1627
			UPDATE {db_prefix}admin_info_files
1628
			SET data = SUBSTRING({string:file_data}, 1, 65534)
1629
			WHERE id_file = {int:id_file}',
1630
			array(
1631
				'id_file' => $ID_FILE,
1632
				'file_data' => $file_data,
1633
			)
1634
		);
1635
	}
1636
	return true;
1637
}
1638
1639
function convertSettingsToTheme()
1640
{
1641
	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...
1642
1643
	$values = array(
1644
		'show_latest_member' => @$GLOBALS['showlatestmember'],
1645
		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
1646
		'show_modify' => @$GLOBALS['showmodify'],
1647
		'show_user_images' => @$GLOBALS['showuserpic'],
1648
		'show_blurb' => @$GLOBALS['showusertext'],
1649
		'show_gender' => @$GLOBALS['showgenderimage'],
1650
		'show_newsfader' => @$GLOBALS['shownewsfader'],
1651
		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
1652
		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
1653
		'linktree_link' => @$GLOBALS['curposlinks'],
1654
		'show_profile_buttons' => @$GLOBALS['profilebutton'],
1655
		'show_mark_read' => @$GLOBALS['showmarkread'],
1656
		'newsfader_time' => @$GLOBALS['fadertime'],
1657
		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
1658
		'enable_news' => @$GLOBALS['enable_news'],
1659
		'return_to_post' => @$modSettings['returnToPost'],
1660
	);
1661
1662
	$themeData = array();
1663
	foreach ($values as $variable => $value)
1664
	{
1665
		if (!isset($value) || $value === null)
1666
			$value = 0;
1667
1668
		$themeData[] = array(0, 1, $variable, $value);
1669
	}
1670 View Code Duplication
	if (!empty($themeData))
1671
	{
1672
		$smcFunc['db_insert']('ignore',
1673
			$db_prefix . 'themes',
1674
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
1675
			$themeData,
1676
			array('id_member', 'id_theme', 'variable')
1677
		);
1678
	}
1679
}
1680
1681
// This function only works with MySQL but that's fine as it is only used for v1.0.
1682
function convertSettingstoOptions()
1683
{
1684
	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...
1685
1686
	// Format: new_setting -> old_setting_name.
1687
	$values = array(
1688
		'calendar_start_day' => 'cal_startmonday',
1689
		'view_newest_first' => 'viewNewestFirst',
1690
		'view_newest_pm_first' => 'viewNewestFirst',
1691
	);
1692
1693
	foreach ($values as $variable => $value)
1694
	{
1695
		if (empty($modSettings[$value[0]]))
1696
			continue;
1697
1698
		$smcFunc['db_query']('', '
1699
			INSERT IGNORE INTO {db_prefix}themes
1700
				(id_member, id_theme, variable, value)
1701
			SELECT id_member, 1, {string:variable}, {string:value}
1702
			FROM {db_prefix}members',
1703
			array(
1704
				'variable' => $variable,
1705
				'value' => $modSettings[$value[0]],
1706
				'db_error_skip' => true,
1707
			)
1708
		);
1709
1710
		$smcFunc['db_query']('', '
1711
			INSERT IGNORE INTO {db_prefix}themes
1712
				(id_member, id_theme, variable, value)
1713
			VALUES (-1, 1, {string:variable}, {string:value})',
1714
			array(
1715
				'variable' => $variable,
1716
				'value' => $modSettings[$value[0]],
1717
				'db_error_skip' => true,
1718
			)
1719
		);
1720
	}
1721
}
1722
1723
function php_version_check()
1724
{
1725
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
1726
}
1727
1728
function db_version_check()
1729
{
1730
	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...
1731
1732
	$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...
1733
	$curver = preg_replace('~\-.+?$~', '', $curver);
1734
1735
	return version_compare($databases[$db_type]['version'], $curver, '<=');
1736
}
1737
1738
function fixRelativePath($path)
1739
{
1740
	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...
1741
1742
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
1743
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
1744
}
1745
1746
function parse_sql($filename)
1747
{
1748
	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...
1749
	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...
1750
1751
/*
1752
	Failure allowed on:
1753
		- INSERT INTO but not INSERT IGNORE INTO.
1754
		- UPDATE IGNORE but not UPDATE.
1755
		- ALTER TABLE and ALTER IGNORE TABLE.
1756
		- DROP TABLE.
1757
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
1758
1759
	If a comment...
1760
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
1761
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
1762
		- is only ---#, it is "done." and then a break - only shown in debug.
1763
		- begins with ---{ it is a code block terminating at ---}.
1764
1765
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
1766
1767
	Replaces the following variables:
1768
		- {$boarddir}
1769
		- {$boardurl}
1770
		- {$db_prefix}
1771
		- {$db_collation}
1772
*/
1773
1774
	// May want to use extended functionality.
1775
	db_extend();
1776
	db_extend('packages');
1777
1778
	// Our custom error handler - does nothing but does stop public errors from XML!
1779
	set_error_handler(
1780
		function ($errno, $errstr, $errfile, $errline) use ($support_js)
1781
		{
1782
			if ($support_js)
1783
				return true;
1784
			else
1785
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
1786
		}
1787
	);
1788
1789
	// If we're on MySQL, set {db_collation}; this approach is used throughout upgrade_2-0_mysql.php to set new tables to utf8
1790
	// Note it is expected to be in the format: ENGINE=MyISAM{$db_collation};
1791
	if ($db_type == 'mysql')
1792
		$db_collation = ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1793
	else
1794
		$db_collation = '';
1795
1796
	$endl = $command_line ? "\n" : '<br>' . "\n";
1797
1798
	$lines = file($filename);
1799
1800
	$current_type = 'sql';
1801
	$current_data = '';
1802
	$substep = 0;
1803
	$last_step = '';
1804
1805
	// Make sure all newly created tables will have the proper characters set; this approach is used throughout upgrade_2-1_mysql.php
1806
	if (isset($db_character_set) && $db_character_set === 'utf8')
1807
		$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
1808
1809
	// Count the total number of steps within this file - for progress.
1810
	$file_steps = substr_count(implode('', $lines), '---#');
1811
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
1812
	$upcontext['debug_items'] = $file_steps;
1813
	$upcontext['current_item_num'] = 0;
1814
	$upcontext['current_item_name'] = '';
1815
	$upcontext['current_debug_item_num'] = 0;
1816
	$upcontext['current_debug_item_name'] = '';
1817
	// This array keeps a record of what we've done in case java is dead...
1818
	$upcontext['actioned_items'] = array();
1819
1820
	$done_something = false;
1821
1822
	foreach ($lines as $line_number => $line)
1823
	{
1824
		$do_current = $substep >= $_GET['substep'];
1825
1826
		// Get rid of any comments in the beginning of the line...
1827
		if (substr(trim($line), 0, 2) === '/*')
1828
			$line = preg_replace('~/\*.+?\*/~', '', $line);
1829
1830
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
1831
		if ($is_debug && !$support_js && $command_line)
1832
			flush();
1833
1834
		if (trim($line) === '')
1835
			continue;
1836
1837
		if (trim(substr($line, 0, 3)) === '---')
1838
		{
1839
			$type = substr($line, 3, 1);
1840
1841
			// An error??
1842
			if (trim($current_data) != '' && $type !== '}')
1843
			{
1844
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
1845
				if ($command_line)
1846
					echo $upcontext['error_message'];
1847
			}
1848
1849
			if ($type == ' ')
1850
			{
1851
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
1852
				{
1853
					echo ' Successful.', $endl;
1854
					flush();
1855
				}
1856
1857
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
1858
				$upcontext['current_item_num']++;
1859
				$upcontext['current_item_name'] = $last_step;
1860
1861
				if ($do_current)
1862
				{
1863
					$upcontext['actioned_items'][] = $last_step;
1864
					if ($command_line)
1865
						echo ' * ';
1866
				}
1867
			}
1868
			elseif ($type == '#')
1869
			{
1870
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
1871
1872
				$upcontext['current_debug_item_num']++;
1873
				if (trim($line) != '---#')
1874
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
1875
1876
				// Have we already done something?
1877
				if (isset($_GET['xml']) && $done_something)
1878
				{
1879
					restore_error_handler();
1880
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
1881
				}
1882
1883
				if ($do_current)
1884
				{
1885
					if (trim($line) == '---#' && $command_line)
1886
						echo ' done.', $endl;
1887
					elseif ($command_line)
1888
						echo ' +++ ', rtrim(substr($line, 4));
1889
					elseif (trim($line) != '---#')
1890
					{
1891
						if ($is_debug)
1892
							$upcontext['actioned_items'][] = htmlspecialchars(rtrim(substr($line, 4)));
1893
					}
1894
				}
1895
1896
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
1897
				{
1898
					if ($command_line)
1899
						echo ' * ';
1900
					else
1901
						$upcontext['actioned_items'][] = $last_step;
1902
				}
1903
1904
				// Small step - only if we're actually doing stuff.
1905
				if ($do_current)
1906
					nextSubstep(++$substep);
1907
				else
1908
					$substep++;
1909
			}
1910
			elseif ($type == '{')
1911
				$current_type = 'code';
1912
			elseif ($type == '}')
1913
			{
1914
				$current_type = 'sql';
1915
1916
				if (!$do_current)
1917
				{
1918
					$current_data = '';
1919
					continue;
1920
				}
1921
1922
				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...
1923
				{
1924
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
1925
					if ($command_line)
1926
						echo $upcontext['error_message'];
1927
				}
1928
1929
				// Done with code!
1930
				$current_data = '';
1931
				$done_something = true;
1932
			}
1933
1934
			continue;
1935
		}
1936
1937
		$current_data .= $line;
1938
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
1939
		{
1940
			if ((!$support_js || isset($_GET['xml'])))
1941
			{
1942
				if (!$do_current)
1943
				{
1944
					$current_data = '';
1945
					continue;
1946
				}
1947
1948
				$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));
1949
1950
				upgrade_query($current_data);
1951
1952
				// @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...
1953
				/*
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...
1954
				$result = $smcFunc['db_query']('', $current_data, false, false);
1955
				// Went wrong?
1956
				if (!$result)
1957
				{
1958
					// Bit of a bodge - do we want the error?
1959
					if (!empty($upcontext['return_error']))
1960
					{
1961
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
1962
						return false;
1963
					}
1964
				}*/
1965
				$done_something = true;
1966
			}
1967
			$current_data = '';
1968
		}
1969
		// If this is xml based and we're just getting the item name then that's grand.
1970
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
1971
		{
1972
			restore_error_handler();
1973
			return false;
1974
		}
1975
1976
		// Clean up by cleaning any step info.
1977
		$step_progress = array();
1978
		$custom_warning = '';
1979
	}
1980
1981
	// Put back the error handler.
1982
	restore_error_handler();
1983
1984
	if ($command_line)
1985
	{
1986
		echo ' Successful.' . "\n";
1987
		flush();
1988
	}
1989
1990
	$_GET['substep'] = 0;
1991
	return true;
1992
}
1993
1994
function upgrade_query($string, $unbuffered = false)
1995
{
1996
	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...
1997
	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...
1998
1999
	// Get the query result - working around some SMF specific security - just this once!
2000
	$modSettings['disableQueryCheck'] = true;
2001
	$db_unbuffered = $unbuffered;
2002
	$ignore_insert_error = false;
2003
2004
	// If we got an old pg version and use a insert ignore query
2005
	if ($db_type == 'postgresql' && !$smcFunc['db_native_replace']() && strpos($string, 'ON CONFLICT DO NOTHING') !== false)
2006
	{
2007
		$ignore_insert_error = true;
2008
		$string = str_replace('ON CONFLICT DO NOTHING', '', $string);
2009
	}
2010
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
2011
	$db_unbuffered = false;
2012
2013
	// Failure?!
2014
	if ($result !== false)
2015
		return $result;
2016
2017
	$db_error_message = $smcFunc['db_error']($db_connection);
2018
	// If MySQL we do something more clever.
2019
	if ($db_type == 'mysql')
2020
	{
2021
		$mysqli_errno = mysqli_errno($db_connection);
2022
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR'));
2023
2024
		// Error numbers:
2025
		//    1016: Can't open file '....MYI'
2026
		//    1050: Table already exists.
2027
		//    1054: Unknown column name.
2028
		//    1060: Duplicate column name.
2029
		//    1061: Duplicate key name.
2030
		//    1062: Duplicate entry for unique key.
2031
		//    1068: Multiple primary keys.
2032
		//    1072: Key column '%s' doesn't exist in table.
2033
		//    1091: Can't drop key, doesn't exist.
2034
		//    1146: Table doesn't exist.
2035
		//    2013: Lost connection to server during query.
2036
2037
		if ($mysqli_errno == 1016)
2038
		{
2039
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
2040
			{
2041
				mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
2042
				$result = mysqli_query($db_connection, $string);
2043
				if ($result !== false)
2044
					return $result;
2045
			}
2046
		}
2047
		elseif ($mysqli_errno == 2013)
2048
		{
2049
			$db_connection = mysqli_connect($db_server, $db_user, $db_passwd);
2050
			mysqli_select_db($db_connection, $db_name);
2051
			if ($db_connection)
2052
			{
2053
				$result = mysqli_query($db_connection, $string);
2054
				if ($result !== false)
2055
					return $result;
2056
			}
2057
		}
2058
		// Duplicate column name... should be okay ;).
2059 View Code Duplication
		elseif (in_array($mysqli_errno, array(1060, 1061, 1068, 1091)))
2060
			return false;
2061
		// Duplicate insert... make sure it's the proper type of query ;).
2062 View Code Duplication
		elseif (in_array($mysqli_errno, array(1054, 1062, 1146)) && $error_query)
2063
			return false;
2064
		// Creating an index on a non-existent column.
2065
		elseif ($mysqli_errno == 1072)
2066
			return false;
2067
		elseif ($mysqli_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
2068
			return false;
2069
	}
2070
	// If a table already exists don't go potty.
2071
	else
2072
	{
2073
		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U')))
2074
		{
2075
			if (strpos($db_error_message, 'exist') !== false)
2076
				return true;
2077
		}
2078
		elseif (strpos(trim($string), 'INSERT ') !== false)
2079
		{
2080
			if (strpos($db_error_message, 'duplicate') !== false || $ignore_insert_error)
2081
				return true;
2082
		}
2083
	}
2084
2085
	// Get the query string so we pass everything.
2086
	$query_string = '';
2087
	foreach ($_GET as $k => $v)
2088
		$query_string .= ';' . $k . '=' . $v;
2089
	if (strlen($query_string) != 0)
2090
		$query_string = '?' . substr($query_string, 1);
2091
2092
	if ($command_line)
2093
	{
2094
		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
2095
		die;
2096
	}
2097
2098
	// Bit of a bodge - do we want the error?
2099
	if (!empty($upcontext['return_error']))
2100
	{
2101
		$upcontext['error_message'] = $db_error_message;
2102
		$upcontext['error_string'] = $string;
2103
		return false;
2104
	}
2105
2106
	// Otherwise we have to display this somewhere appropriate if possible.
2107
	$upcontext['forced_error_message'] = '
2108
			<strong>Unsuccessful!</strong><br>
2109
2110
			<div style="margin: 2ex;">
2111
				This query:
2112
				<blockquote><pre>' . nl2br(htmlspecialchars(trim($string))) . ';</pre></blockquote>
2113
2114
				Caused the error:
2115
				<blockquote>' . nl2br(htmlspecialchars($db_error_message)) . '</blockquote>
2116
			</div>
2117
2118
			<form action="' . $upgradeurl . $query_string . '" method="post">
2119
				<input type="submit" value="Try again" class="button">
2120
			</form>
2121
		</div>';
2122
2123
	upgradeExit();
2124
}
2125
2126
// This performs a table alter, but does it unbuffered so the script can time out professionally.
2127
function protected_alter($change, $substep, $is_test = false)
2128
{
2129
	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...
2130
2131
	db_extend('packages');
2132
2133
	// Firstly, check whether the current index/column exists.
2134
	$found = false;
2135
	if ($change['type'] === 'column')
2136
	{
2137
		$columns = $smcFunc['db_list_columns']('{db_prefix}' . $change['table'], true);
2138
		foreach ($columns as $column)
2139
		{
2140
			// Found it?
2141
			if ($column['name'] === $change['name'])
2142
			{
2143
				$found |= 1;
2144
				// Do some checks on the data if we have it set.
2145
				if (isset($change['col_type']))
2146
					$found &= $change['col_type'] === $column['type'];
2147
				if (isset($change['null_allowed']))
2148
					$found &= $column['null'] == $change['null_allowed'];
2149
				if (isset($change['default']))
2150
					$found &= $change['default'] === $column['default'];
2151
			}
2152
		}
2153
	}
2154
	elseif ($change['type'] === 'index')
2155
	{
2156
		$request = upgrade_query('
2157
			SHOW INDEX
2158
			FROM ' . $db_prefix . $change['table']);
2159
		if ($request !== false)
2160
		{
2161
			$cur_index = array();
2162
2163
			while ($row = $smcFunc['db_fetch_assoc']($request))
2164
				if ($row['Key_name'] === $change['name'])
2165
					$cur_index[(int) $row['Seq_in_index']] = $row['Column_name'];
2166
2167
			ksort($cur_index, SORT_NUMERIC);
2168
			$found = array_values($cur_index) === $change['target_columns'];
2169
2170
			$smcFunc['db_free_result']($request);
2171
		}
2172
	}
2173
2174
	// If we're trying to add and it's added, we're done.
2175
	if ($found && in_array($change['method'], array('add', 'change')))
2176
		return true;
2177
	// Otherwise if we're removing and it wasn't found we're also done.
2178
	elseif (!$found && in_array($change['method'], array('remove', 'change_remove')))
2179
		return true;
2180
	// Otherwise is it just a test?
2181
	elseif ($is_test)
2182
		return false;
2183
2184
	// Not found it yet? Bummer! How about we see if we're currently doing it?
2185
	$running = false;
2186
	$found = false;
2187
	while (1 == 1)
2188
	{
2189
		$request = upgrade_query('
2190
			SHOW FULL PROCESSLIST');
2191
		while ($row = $smcFunc['db_fetch_assoc']($request))
2192
		{
2193
			if (strpos($row['Info'], 'ALTER TABLE ' . $db_prefix . $change['table']) !== false && strpos($row['Info'], $change['text']) !== false)
2194
				$found = true;
2195
		}
2196
2197
		// Can't find it? Then we need to run it fools!
2198
		if (!$found && !$running)
2199
		{
2200
			$smcFunc['db_free_result']($request);
2201
2202
			$success = upgrade_query('
2203
				ALTER TABLE ' . $db_prefix . $change['table'] . '
2204
				' . $change['text'], true) !== false;
2205
2206
			if (!$success)
2207
				return false;
2208
2209
			// Return
2210
			$running = true;
2211
		}
2212
		// What if we've not found it, but we'd ran it already? Must of completed.
2213
		elseif (!$found)
2214
		{
2215
			$smcFunc['db_free_result']($request);
2216
			return true;
2217
		}
2218
2219
		// Pause execution for a sec or three.
2220
		sleep(3);
2221
2222
		// Can never be too well protected.
2223
		nextSubstep($substep);
2224
	}
2225
2226
	// Protect it.
2227
	nextSubstep($substep);
2228
}
2229
2230
/**
2231
 * Alter a text column definition preserving its character set.
2232
 *
2233
 * @param array $change
2234
 * @param int $substep
2235
 */
2236
function textfield_alter($change, $substep)
2237
{
2238
	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...
2239
2240
	$request = $smcFunc['db_query']('', '
2241
		SHOW FULL COLUMNS
2242
		FROM {db_prefix}' . $change['table'] . '
2243
		LIKE {string:column}',
2244
		array(
2245
			'column' => $change['column'],
2246
			'db_error_skip' => true,
2247
		)
2248
	);
2249
	if ($smcFunc['db_num_rows']($request) === 0)
2250
		die('Unable to find column ' . $change['column'] . ' inside table ' . $db_prefix . $change['table']);
2251
	$table_row = $smcFunc['db_fetch_assoc']($request);
2252
	$smcFunc['db_free_result']($request);
2253
2254
	// If something of the current column definition is different, fix it.
2255
	$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']);
2256
2257
	// Columns that previously allowed null, need to be converted first.
2258
	$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
2259
2260
	// Get the character set that goes with the collation of the column.
2261 View Code Duplication
	if ($column_fix && !empty($table_row['Collation']))
2262
	{
2263
		$request = $smcFunc['db_query']('', '
2264
			SHOW COLLATION
2265
			LIKE {string:collation}',
2266
			array(
2267
				'collation' => $table_row['Collation'],
2268
				'db_error_skip' => true,
2269
			)
2270
		);
2271
		// No results? Just forget it all together.
2272
		if ($smcFunc['db_num_rows']($request) === 0)
2273
			unset($table_row['Collation']);
2274
		else
2275
			$collation_info = $smcFunc['db_fetch_assoc']($request);
2276
		$smcFunc['db_free_result']($request);
2277
	}
2278
2279
	if ($column_fix)
2280
	{
2281
		// Make sure there are no NULL's left.
2282
		if ($null_fix)
2283
			$smcFunc['db_query']('', '
2284
				UPDATE {db_prefix}' . $change['table'] . '
2285
				SET ' . $change['column'] . ' = {string:default}
2286
				WHERE ' . $change['column'] . ' IS NULL',
2287
				array(
2288
					'default' => isset($change['default']) ? $change['default'] : '',
2289
					'db_error_skip' => true,
2290
				)
2291
			);
2292
2293
		// Do the actual alteration.
2294
		$smcFunc['db_query']('', '
2295
			ALTER TABLE {db_prefix}' . $change['table'] . '
2296
			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}' : ''),
2297
			array(
2298
				'default' => isset($change['default']) ? $change['default'] : '',
2299
				'db_error_skip' => true,
2300
			)
2301
		);
2302
	}
2303
	nextSubstep($substep);
2304
}
2305
2306
// Check if we need to alter this query.
2307
function checkChange(&$change)
2308
{
2309
	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...
2310
	static $database_version, $where_field_support;
2311
2312
	// Attempt to find a database_version.
2313
	if (empty($database_version))
2314
	{
2315
		$database_version = $databases[$db_type]['version_check'];
2316
		$where_field_support = $db_type == 'mysql' && version_compare('5.0', $database_version, '<=');
2317
	}
2318
2319
	// Not a column we need to check on?
2320
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
2321
		return;
2322
2323
	// Break it up you (six|seven).
2324
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
2325
2326
	// Can we support a shortcut method?
2327
	if ($where_field_support)
2328
	{
2329
		// Get the details about this change.
2330
		$request = $smcFunc['db_query']('', '
2331
			SHOW FIELDS
2332
			FROM {db_prefix}{raw:table}
2333
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
2334
			array(
2335
				'table' => $change['table'],
2336
				'old_name' => $temp[1],
2337
				'new_name' => $temp[2],
2338
		));
2339
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2340
		if ($smcFunc['db_num_rows'] != 1)
2341
			return;
2342
2343
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
2344
		$smcFunc['db_free_result']($request);
2345
	}
2346
	else
2347
	{
2348
		// Do this the old fashion, sure method way.
2349
		$request = $smcFunc['db_query']('', '
2350
			SHOW FIELDS
2351
			FROM {db_prefix}{raw:table}',
2352
			array(
2353
				'table' => $change['table'],
2354
		));
2355
		// Mayday!
2356
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2357
		if ($smcFunc['db_num_rows'] == 0)
2358
			return;
2359
2360
		// Oh where, oh where has my little field gone. Oh where can it be...
2361
		while ($row = $smcFunc['db_query']($request))
2362
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
2363
			{
2364
				$current_type = $row['Type'];
2365
				break;
2366
			}
2367
	}
2368
2369
	// If this doesn't match, the column may of been altered for a reason.
2370
	if (trim($current_type) != trim($temp[3]))
2371
		$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...
2372
2373
	// Piece this back together.
2374
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
2375
}
2376
2377
// The next substep.
2378
function nextSubstep($substep)
2379
{
2380
	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...
2381
	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...
2382
2383
	if ($_GET['substep'] < $substep)
2384
		$_GET['substep'] = $substep;
2385
2386
	if ($command_line)
2387
	{
2388
		if (time() - $start_time > 1 && empty($is_debug))
2389
		{
2390
			echo '.';
2391
			$start_time = time();
2392
		}
2393
		return;
2394
	}
2395
2396
	@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...
2397
	if (function_exists('apache_reset_timeout'))
2398
		@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...
2399
2400
	if (time() - $start_time <= $timeLimitThreshold)
2401
		return;
2402
2403
	// Do we have some custom step progress stuff?
2404
	if (!empty($step_progress))
2405
	{
2406
		$upcontext['substep_progress'] = 0;
2407
		$upcontext['substep_progress_name'] = $step_progress['name'];
2408
		if ($step_progress['current'] > $step_progress['total'])
2409
			$upcontext['substep_progress'] = 99.9;
2410
		else
2411
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2412
2413
		// Make it nicely rounded.
2414
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2415
	}
2416
2417
	// If this is XML we just exit right away!
2418
	if (isset($_GET['xml']))
2419
		return upgradeExit();
2420
2421
	// We're going to pause after this!
2422
	$upcontext['pause'] = true;
2423
2424
	$upcontext['query_string'] = '';
2425
	foreach ($_GET as $k => $v)
2426
	{
2427
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2428
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2429
	}
2430
2431
	// Custom warning?
2432
	if (!empty($custom_warning))
2433
		$upcontext['custom_warning'] = $custom_warning;
2434
2435
	upgradeExit();
2436
}
2437
2438
function cmdStep0()
2439
{
2440
	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...
2441
	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...
2442
	$start_time = time();
2443
2444
	ob_end_clean();
2445
	ob_implicit_flush(true);
2446
	@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...
2447
2448
	if (!isset($_SERVER['argv']))
2449
		$_SERVER['argv'] = array();
2450
	$_GET['maint'] = 1;
2451
2452
	foreach ($_SERVER['argv'] as $i => $arg)
2453
	{
2454
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2455
			$_GET['lang'] = $match[1];
2456
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2457
			continue;
2458
		elseif ($arg == '--no-maintenance')
2459
			$_GET['maint'] = 0;
2460
		elseif ($arg == '--debug')
2461
			$is_debug = true;
2462
		elseif ($arg == '--backup')
2463
			$_POST['backup'] = 1;
2464
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2465
			$_GET['conv'] = 1;
2466
		elseif ($i != 0)
2467
		{
2468
			echo 'SMF Command-line Upgrader
2469
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2470
2471
    --language=LANG         Reset the forum\'s language to LANG.
2472
    --no-maintenance        Don\'t put the forum into maintenance mode.
2473
    --debug                 Output debugging information.
2474
    --backup                Create backups of tables with "backup_" prefix.';
2475
			echo "\n";
2476
			exit;
2477
		}
2478
	}
2479
2480
	if (!php_version_check())
2481
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
2482
	if (!db_version_check())
2483
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2484
2485
	// Do some checks to make sure they have proper privileges
2486
	db_extend('packages');
2487
2488
	// CREATE
2489
	$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');
2490
2491
	// ALTER
2492
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
2493
2494
	// DROP
2495
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2496
2497
	// Sorry... we need CREATE, ALTER and DROP
2498 View Code Duplication
	if (!$create || !$alter || !$drop)
2499
		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...
2500
2501
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2502
		&& @file_exists($sourcedir . '/QueryString.php')
2503
		&& @file_exists($sourcedir . '/ManageBoards.php');
2504
	if (!$check && !isset($modSettings['smfVersion']))
2505
		print_error('Error: Some files are missing or out-of-date.', true);
2506
2507
	// Do a quick version spot check.
2508
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
2509
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2510
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2511
		print_error('Error: Some files have not yet been updated properly.');
2512
2513
	// Make sure Settings.php is writable.
2514
	quickFileWritable($boarddir . '/Settings.php');
2515
	if (!is_writable($boarddir . '/Settings.php'))
2516
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2517
2518
	// Make sure Settings_bak.php is writable.
2519
	quickFileWritable($boarddir . '/Settings_bak.php');
2520
	if (!is_writable($boarddir . '/Settings_bak.php'))
2521
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2522
2523 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
2524
		print_error('Error: Unable to obtain write access to "agreement.txt".');
2525
	elseif (isset($modSettings['agreement']))
2526
	{
2527
		$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...
2528
		fwrite($fp, $modSettings['agreement']);
2529
		fclose($fp);
2530
	}
2531
2532
	// Make sure Themes is writable.
2533
	quickFileWritable($modSettings['theme_dir']);
2534
2535
	if (!is_writable($modSettings['theme_dir']) && !isset($modSettings['smfVersion']))
2536
		print_error('Error: Unable to obtain write access to "Themes".');
2537
2538
	// Make sure cache directory exists and is writable!
2539
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
2540
	if (!file_exists($cachedir_temp))
2541
		@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...
2542
2543
	// Make sure the cache temp dir is writable.
2544
	quickFileWritable($cachedir_temp);
2545
2546
	if (!is_writable($cachedir_temp))
2547
		print_error('Error: Unable to obtain write access to "cache".', true);
2548
2549
	// Make sure db_last_error.php is writable.
2550
	quickFileWritable($cachedir_temp . '/db_last_error.php');
2551
	if (!is_writable($cachedir_temp . '/db_last_error.php'))
2552
		print_error('Error: Unable to obtain write access to "db_last_error.php".');
2553
2554
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
2555
		print_error('Error: Unable to find language files!', true);
2556
	else
2557
	{
2558
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
2559
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
2560
2561
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
2562
			print_error('Error: Language files out of date.', true);
2563
		if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
2564
			print_error('Error: Install language is missing for selected language.', true);
2565
2566
		// Otherwise include it!
2567
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
2568
	}
2569
2570
	// Make sure we skip the HTML for login.
2571
	$_POST['upcont'] = true;
2572
	$upcontext['current_step'] = 1;
2573
}
2574
2575
/**
2576
 * Handles converting your database to UTF-8
2577
 */
2578
function ConvertUtf8()
2579
{
2580
	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...
2581
	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...
2582
2583
	// Done it already?
2584
	if (!empty($_POST['utf8_done']))
2585
		return true;
2586
2587
	// First make sure they aren't already on UTF-8 before we go anywhere...
2588
	if ($db_type == 'postgresql' || ($db_character_set === 'utf8' && !empty($modSettings['global_character_set']) && $modSettings['global_character_set'] === 'UTF-8'))
2589
	{
2590
		$smcFunc['db_insert']('replace',
2591
			'{db_prefix}settings',
2592
			array('variable' => 'string', 'value' => 'string'),
2593
			array(array('global_character_set', 'UTF-8')),
2594
			array('variable')
2595
		);
2596
2597
		return true;
2598
	}
2599
	else
2600
	{
2601
		$upcontext['page_title'] = $txt['converting_utf8'];
2602
		$upcontext['sub_template'] = isset($_GET['xml']) ? 'convert_xml' : 'convert_utf8';
2603
2604
		// The character sets used in SMF's language files with their db equivalent.
2605
		$charsets = array(
2606
			// Armenian
2607
			'armscii8' => 'armscii8',
2608
			// Chinese-traditional.
2609
			'big5' => 'big5',
2610
			// Chinese-simplified.
2611
			'gbk' => 'gbk',
2612
			// West European.
2613
			'ISO-8859-1' => 'latin1',
2614
			// Romanian.
2615
			'ISO-8859-2' => 'latin2',
2616
			// Turkish.
2617
			'ISO-8859-9' => 'latin5',
2618
			// Latvian
2619
			'ISO-8859-13' => 'latin7',
2620
			// West European with Euro sign.
2621
			'ISO-8859-15' => 'latin9',
2622
			// Thai.
2623
			'tis-620' => 'tis620',
2624
			// Persian, Chinese, etc.
2625
			'UTF-8' => 'utf8',
2626
			// Russian.
2627
			'windows-1251' => 'cp1251',
2628
			// Greek.
2629
			'windows-1253' => 'utf8',
2630
			// Hebrew.
2631
			'windows-1255' => 'utf8',
2632
			// Arabic.
2633
			'windows-1256' => 'cp1256',
2634
		);
2635
2636
		// Get a list of character sets supported by your MySQL server.
2637
		$request = $smcFunc['db_query']('', '
2638
			SHOW CHARACTER SET',
2639
			array(
2640
			)
2641
		);
2642
		$db_charsets = array();
2643
		while ($row = $smcFunc['db_fetch_assoc']($request))
2644
			$db_charsets[] = $row['Charset'];
2645
2646
		$smcFunc['db_free_result']($request);
2647
2648
		// Character sets supported by both MySQL and SMF's language files.
2649
		$charsets = array_intersect($charsets, $db_charsets);
2650
2651
		// Use the messages.body column as indicator for the database charset.
2652
		$request = $smcFunc['db_query']('', '
2653
			SHOW FULL COLUMNS
2654
			FROM {db_prefix}messages
2655
			LIKE {string:body_like}',
2656
			array(
2657
				'body_like' => 'body',
2658
			)
2659
		);
2660
		$column_info = $smcFunc['db_fetch_assoc']($request);
2661
		$smcFunc['db_free_result']($request);
2662
2663
		// A collation looks like latin1_swedish. We only need the character set.
2664
		list($upcontext['database_charset']) = explode('_', $column_info['Collation']);
2665
		$upcontext['database_charset'] = in_array($upcontext['database_charset'], $charsets) ? array_search($upcontext['database_charset'], $charsets) : $upcontext['database_charset'];
2666
2667
		// Detect whether a fulltext index is set.
2668
		$request = $smcFunc['db_query']('', '
2669
			SHOW INDEX
2670
			FROM {db_prefix}messages',
2671
			array(
2672
			)
2673
		);
2674
2675
		$upcontext['dropping_index'] = false;
2676
2677
		// If there's a fulltext index, we need to drop it first...
2678 View Code Duplication
		if ($request !== false || $smcFunc['db_num_rows']($request) != 0)
2679
		{
2680
			while ($row = $smcFunc['db_fetch_assoc']($request))
2681
				if ($row['Column_name'] == 'body' && (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT' || isset($row['Comment']) && $row['Comment'] == 'FULLTEXT'))
2682
					$upcontext['fulltext_index'][] = $row['Key_name'];
2683
			$smcFunc['db_free_result']($request);
2684
2685
			if (isset($upcontext['fulltext_index']))
2686
				$upcontext['fulltext_index'] = array_unique($upcontext['fulltext_index']);
2687
		}
2688
2689
		// Drop it and make a note...
2690
		if (!empty($upcontext['fulltext_index']))
2691
		{
2692
			$upcontext['dropping_index'] = true;
2693
2694
			$smcFunc['db_query']('', '
2695
			ALTER TABLE {db_prefix}messages
2696
			DROP INDEX ' . implode(',
2697
			DROP INDEX ', $upcontext['fulltext_index']),
2698
				array(
2699
					'db_error_skip' => true,
2700
				)
2701
			);
2702
2703
			// Update the settings table
2704
			$smcFunc['db_insert']('replace',
2705
				'{db_prefix}settings',
2706
				array('variable' => 'string', 'value' => 'string'),
2707
				array('db_search_index', ''),
2708
				array('variable')
2709
			);
2710
		}
2711
2712
		// Figure out what charset we should be converting from...
2713
		$lang_charsets = array(
2714
			'arabic' => 'windows-1256',
2715
			'armenian_east' => 'armscii-8',
2716
			'armenian_west' => 'armscii-8',
2717
			'azerbaijani_latin' => 'ISO-8859-9',
2718
			'bangla' => 'UTF-8',
2719
			'belarusian' => 'ISO-8859-5',
2720
			'bulgarian' => 'windows-1251',
2721
			'cambodian' => 'UTF-8',
2722
			'chinese_simplified' => 'gbk',
2723
			'chinese_traditional' => 'big5',
2724
			'croation' => 'ISO-8859-2',
2725
			'czech' => 'ISO-8859-2',
2726
			'czech_informal' => 'ISO-8859-2',
2727
			'english_pirate' => 'UTF-8',
2728
			'esperanto' => 'ISO-8859-3',
2729
			'estonian' => 'ISO-8859-15',
2730
			'filipino_tagalog' => 'UTF-8',
2731
			'filipino_vasayan' => 'UTF-8',
2732
			'georgian' => 'UTF-8',
2733
			'greek' => 'ISO-8859-3',
2734
			'hebrew' => 'windows-1255',
2735
			'hungarian' => 'ISO-8859-2',
2736
			'irish' => 'UTF-8',
2737
			'japanese' => 'UTF-8',
2738
			'khmer' => 'UTF-8',
2739
			'korean' => 'UTF-8',
2740
			'kurdish_kurmanji' => 'ISO-8859-9',
2741
			'kurdish_sorani' => 'windows-1256',
2742
			'lao' => 'tis-620',
2743
			'latvian' => 'ISO-8859-13',
2744
			'lithuanian' => 'ISO-8859-4',
2745
			'macedonian' => 'UTF-8',
2746
			'malayalam' => 'UTF-8',
2747
			'mongolian' => 'UTF-8',
2748
			'nepali' => 'UTF-8',
2749
			'persian' => 'UTF-8',
2750
			'polish' => 'ISO-8859-2',
2751
			'romanian' => 'ISO-8859-2',
2752
			'russian' => 'windows-1252',
2753
			'sakha' => 'UTF-8',
2754
			'serbian_cyrillic' => 'ISO-8859-5',
2755
			'serbian_latin' => 'ISO-8859-2',
2756
			'sinhala' => 'UTF-8',
2757
			'slovak' => 'ISO-8859-2',
2758
			'slovenian' => 'ISO-8859-2',
2759
			'telugu' => 'UTF-8',
2760
			'thai' => 'tis-620',
2761
			'turkish' => 'ISO-8859-9',
2762
			'turkmen' => 'ISO-8859-9',
2763
			'ukranian' => 'windows-1251',
2764
			'urdu' => 'UTF-8',
2765
			'uzbek_cyrillic' => 'ISO-8859-5',
2766
			'uzbek_latin' => 'ISO-8859-5',
2767
			'vietnamese' => 'UTF-8',
2768
			'yoruba' => 'UTF-8'
2769
		);
2770
2771
		// Default to ISO-8859-1 unless we detected another supported charset
2772
		$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';
2773
2774
		$upcontext['charset_list'] = array_keys($charsets);
2775
2776
		// Translation table for the character sets not native for MySQL.
2777
		$translation_tables = array(
2778
			'windows-1255' => array(
2779
				'0x81' => '\'\'',		'0x8A' => '\'\'',		'0x8C' => '\'\'',
2780
				'0x8D' => '\'\'',		'0x8E' => '\'\'',		'0x8F' => '\'\'',
2781
				'0x90' => '\'\'',		'0x9A' => '\'\'',		'0x9C' => '\'\'',
2782
				'0x9D' => '\'\'',		'0x9E' => '\'\'',		'0x9F' => '\'\'',
2783
				'0xCA' => '\'\'',		'0xD9' => '\'\'',		'0xDA' => '\'\'',
2784
				'0xDB' => '\'\'',		'0xDC' => '\'\'',		'0xDD' => '\'\'',
2785
				'0xDE' => '\'\'',		'0xDF' => '\'\'',		'0xFB' => '0xD792',
2786
				'0xFC' => '0xE282AC',		'0xFF' => '0xD6B2',		'0xC2' => '0xFF',
2787
				'0x80' => '0xFC',		'0xE2' => '0xFB',		'0xA0' => '0xC2A0',
2788
				'0xA1' => '0xC2A1',		'0xA2' => '0xC2A2',		'0xA3' => '0xC2A3',
2789
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2790
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2791
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2792
				'0xAF' => '0xC2AF',		'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',
2793
				'0xB2' => '0xC2B2',		'0xB3' => '0xC2B3',		'0xB4' => '0xC2B4',
2794
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2795
				'0xB8' => '0xC2B8',		'0xB9' => '0xC2B9',		'0xBB' => '0xC2BB',
2796
				'0xBC' => '0xC2BC',		'0xBD' => '0xC2BD',		'0xBE' => '0xC2BE',
2797
				'0xBF' => '0xC2BF',		'0xD7' => '0xD7B3',		'0xD1' => '0xD781',
2798
				'0xD4' => '0xD7B0',		'0xD5' => '0xD7B1',		'0xD6' => '0xD7B2',
2799
				'0xE0' => '0xD790',		'0xEA' => '0xD79A',		'0xEC' => '0xD79C',
2800
				'0xED' => '0xD79D',		'0xEE' => '0xD79E',		'0xEF' => '0xD79F',
2801
				'0xF0' => '0xD7A0',		'0xF1' => '0xD7A1',		'0xF2' => '0xD7A2',
2802
				'0xF3' => '0xD7A3',		'0xF5' => '0xD7A5',		'0xF6' => '0xD7A6',
2803
				'0xF7' => '0xD7A7',		'0xF8' => '0xD7A8',		'0xF9' => '0xD7A9',
2804
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2805
				'0x86' => '0xE280A0',	'0x87' => '0xE280A1',	'0x89' => '0xE280B0',
2806
				'0x8B' => '0xE280B9',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2807
				'0x95' => '0xE280A2',	'0x97' => '0xE28094',	'0x99' => '0xE284A2',
2808
				'0xC0' => '0xD6B0',		'0xC1' => '0xD6B1',		'0xC3' => '0xD6B3',
2809
				'0xC4' => '0xD6B4',		'0xC5' => '0xD6B5',		'0xC6' => '0xD6B6',
2810
				'0xC7' => '0xD6B7',		'0xC8' => '0xD6B8',		'0xC9' => '0xD6B9',
2811
				'0xCB' => '0xD6BB',		'0xCC' => '0xD6BC',		'0xCD' => '0xD6BD',
2812
				'0xCE' => '0xD6BE',		'0xCF' => '0xD6BF',		'0xD0' => '0xD780',
2813
				'0xD2' => '0xD782',		'0xE3' => '0xD793',		'0xE4' => '0xD794',
2814
				'0xE5' => '0xD795',		'0xE7' => '0xD797',		'0xE9' => '0xD799',
2815
				'0xFD' => '0xE2808E',	'0xFE' => '0xE2808F',	'0x92' => '0xE28099',
2816
				'0x83' => '0xC692',		'0xD3' => '0xD783',		'0x88' => '0xCB86',
2817
				'0x98' => '0xCB9C',		'0x91' => '0xE28098',	'0x96' => '0xE28093',
2818
				'0xBA' => '0xC3B7',		'0x9B' => '0xE280BA',	'0xAA' => '0xC397',
2819
				'0xA4' => '0xE282AA',	'0xE1' => '0xD791',		'0xE6' => '0xD796',
2820
				'0xE8' => '0xD798',		'0xEB' => '0xD79B',		'0xF4' => '0xD7A4',
2821
				'0xFA' => '0xD7AA',
2822
			),
2823
			'windows-1253' => array(
2824
				'0x81' => '\'\'',			'0x88' => '\'\'',			'0x8A' => '\'\'',
2825
				'0x8C' => '\'\'',			'0x8D' => '\'\'',			'0x8E' => '\'\'',
2826
				'0x8F' => '\'\'',			'0x90' => '\'\'',			'0x98' => '\'\'',
2827
				'0x9A' => '\'\'',			'0x9C' => '\'\'',			'0x9D' => '\'\'',
2828
				'0x9E' => '\'\'',			'0x9F' => '\'\'',			'0xAA' => '\'\'',
2829
				'0xD2' => '0xE282AC',			'0xFF' => '0xCE92',			'0xCE' => '0xCE9E',
2830
				'0xB8' => '0xCE88',		'0xBA' => '0xCE8A',		'0xBC' => '0xCE8C',
2831
				'0xBE' => '0xCE8E',		'0xBF' => '0xCE8F',		'0xC0' => '0xCE90',
2832
				'0xC8' => '0xCE98',		'0xCA' => '0xCE9A',		'0xCC' => '0xCE9C',
2833
				'0xCD' => '0xCE9D',		'0xCF' => '0xCE9F',		'0xDA' => '0xCEAA',
2834
				'0xE8' => '0xCEB8',		'0xEA' => '0xCEBA',		'0xEC' => '0xCEBC',
2835
				'0xEE' => '0xCEBE',		'0xEF' => '0xCEBF',		'0xC2' => '0xFF',
2836
				'0xBD' => '0xC2BD',		'0xED' => '0xCEBD',		'0xB2' => '0xC2B2',
2837
				'0xA0' => '0xC2A0',		'0xA3' => '0xC2A3',		'0xA4' => '0xC2A4',
2838
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2839
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2840
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2841
				'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',		'0xB3' => '0xC2B3',
2842
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2843
				'0xBB' => '0xC2BB',		'0xE2' => '0xCEB2',		'0x80' => '0xD2',
2844
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2845
				'0x86' => '0xE280A0',	'0xA1' => '0xCE85',		'0xA2' => '0xCE86',
2846
				'0x87' => '0xE280A1',	'0x89' => '0xE280B0',	'0xB9' => '0xCE89',
2847
				'0x8B' => '0xE280B9',	'0x91' => '0xE28098',	'0x99' => '0xE284A2',
2848
				'0x92' => '0xE28099',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2849
				'0x95' => '0xE280A2',	'0x96' => '0xE28093',	'0x97' => '0xE28094',
2850
				'0x9B' => '0xE280BA',	'0xAF' => '0xE28095',	'0xB4' => '0xCE84',
2851
				'0xC1' => '0xCE91',		'0xC3' => '0xCE93',		'0xC4' => '0xCE94',
2852
				'0xC5' => '0xCE95',		'0xC6' => '0xCE96',		'0x83' => '0xC692',
2853
				'0xC7' => '0xCE97',		'0xC9' => '0xCE99',		'0xCB' => '0xCE9B',
2854
				'0xD0' => '0xCEA0',		'0xD1' => '0xCEA1',		'0xD3' => '0xCEA3',
2855
				'0xD4' => '0xCEA4',		'0xD5' => '0xCEA5',		'0xD6' => '0xCEA6',
2856
				'0xD7' => '0xCEA7',		'0xD8' => '0xCEA8',		'0xD9' => '0xCEA9',
2857
				'0xDB' => '0xCEAB',		'0xDC' => '0xCEAC',		'0xDD' => '0xCEAD',
2858
				'0xDE' => '0xCEAE',		'0xDF' => '0xCEAF',		'0xE0' => '0xCEB0',
2859
				'0xE1' => '0xCEB1',		'0xE3' => '0xCEB3',		'0xE4' => '0xCEB4',
2860
				'0xE5' => '0xCEB5',		'0xE6' => '0xCEB6',		'0xE7' => '0xCEB7',
2861
				'0xE9' => '0xCEB9',		'0xEB' => '0xCEBB',		'0xF0' => '0xCF80',
2862
				'0xF1' => '0xCF81',		'0xF2' => '0xCF82',		'0xF3' => '0xCF83',
2863
				'0xF4' => '0xCF84',		'0xF5' => '0xCF85',		'0xF6' => '0xCF86',
2864
				'0xF7' => '0xCF87',		'0xF8' => '0xCF88',		'0xF9' => '0xCF89',
2865
				'0xFA' => '0xCF8A',		'0xFB' => '0xCF8B',		'0xFC' => '0xCF8C',
2866
				'0xFD' => '0xCF8D',		'0xFE' => '0xCF8E',
2867
			),
2868
		);
2869
2870
		// Make some preparations.
2871
		if (isset($translation_tables[$upcontext['charset_detected']]))
2872
		{
2873
			$replace = '%field%';
2874
2875
			// Build a huge REPLACE statement...
2876
			foreach ($translation_tables[$upcontext['charset_detected']] as $from => $to)
2877
				$replace = 'REPLACE(' . $replace . ', ' . $from . ', ' . $to . ')';
2878
		}
2879
2880
		// Get a list of table names ahead of time... This makes it easier to set our substep and such
2881
		db_extend();
2882
		$queryTables = $smcFunc['db_list_tables'](false, $db_prefix . '%');
2883
2884
		$upcontext['table_count'] = count($queryTables);
2885
2886
		// What ones have we already done?
2887 View Code Duplication
		foreach ($queryTables as $id => $table)
2888
			if ($id < $_GET['substep'])
2889
				$upcontext['previous_tables'][] = $table;
2890
2891
		$upcontext['cur_table_num'] = $_GET['substep'];
2892
		$upcontext['cur_table_name'] = str_replace($db_prefix, '', $queryTables[$_GET['substep']]);
2893
		$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2894
2895
		// Make sure we're ready & have painted the template before proceeding
2896
		if ($support_js && !isset($_GET['xml'])) {
2897
			$_GET['substep'] = 0;
2898
			return false;
2899
		}
2900
2901
		// We want to start at the first table.
2902
		for ($substep = $_GET['substep'], $n = count($queryTables); $substep < $n; $substep++)
2903
		{
2904
			$table = $queryTables[$substep];
2905
2906
			$getTableStatus = $smcFunc['db_query']('', '
2907
				SHOW TABLE STATUS
2908
				LIKE {string:table_name}',
2909
				array(
2910
					'table_name' => str_replace('_', '\_', $table)
2911
				)
2912
			);
2913
2914
			// Only one row so we can just fetch_assoc and free the result...
2915
			$table_info = $smcFunc['db_fetch_assoc']($getTableStatus);
2916
			$smcFunc['db_free_result']($getTableStatus);
2917
2918
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($queryTables[$substep + 1]) ? $queryTables[$substep + 1] : $queryTables[$substep]));
2919
			$upcontext['cur_table_num'] = $substep + 1;
2920
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2921
2922
			// Do we need to pause?
2923
			nextSubstep($substep);
2924
2925
			// Just to make sure it doesn't time out.
2926
			if (function_exists('apache_reset_timeout'))
2927
				@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...
2928
2929
			$table_charsets = array();
2930
2931
			// Loop through each column.
2932
			$queryColumns = $smcFunc['db_query']('', '
2933
				SHOW FULL COLUMNS
2934
				FROM ' . $table_info['Name'],
2935
				array(
2936
				)
2937
			);
2938
			while ($column_info = $smcFunc['db_fetch_assoc']($queryColumns))
2939
			{
2940
				// Only text'ish columns have a character set and need converting.
2941
				if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false)
2942
				{
2943
					$collation = empty($column_info['Collation']) || $column_info['Collation'] === 'NULL' ? $table_info['Collation'] : $column_info['Collation'];
2944
					if (!empty($collation) && $collation !== 'NULL')
2945
					{
2946
						list($charset) = explode('_', $collation);
2947
2948
						// Build structure of columns to operate on organized by charset; only operate on columns not yet utf8
2949
						if ($charset != 'utf8') {
2950
							if (!isset($table_charsets[$charset]))
2951
								$table_charsets[$charset] = array();
2952
2953
							$table_charsets[$charset][] = $column_info;
2954
						}
2955
					}
2956
				}
2957
			}
2958
			$smcFunc['db_free_result']($queryColumns);
2959
2960
			// Only change the non-utf8 columns identified above
2961
			if (count($table_charsets) > 0)
2962
			{
2963
				$updates_blob = '';
2964
				$updates_text = '';
2965
				foreach ($table_charsets as $charset => $columns)
2966
				{
2967
					if ($charset !== $charsets[$upcontext['charset_detected']])
2968
					{
2969
						foreach ($columns as $column)
2970
						{
2971
							$updates_blob .= '
2972
								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'] . '\'') . ',';
2973
							$updates_text .= '
2974
								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'] . '\'') . ',';
2975
						}
2976
					}
2977
				}
2978
2979
				// Change the columns to binary form.
2980
				$smcFunc['db_query']('', '
2981
					ALTER TABLE {raw:table_name}{raw:updates_blob}',
2982
					array(
2983
						'table_name' => $table_info['Name'],
2984
						'updates_blob' => substr($updates_blob, 0, -1),
2985
					)
2986
				);
2987
2988
				// Convert the character set if MySQL has no native support for it.
2989
				if (isset($translation_tables[$upcontext['charset_detected']]))
2990
				{
2991
					$update = '';
2992
					foreach ($table_charsets as $charset => $columns)
2993
						foreach ($columns as $column)
2994
							$update .= '
2995
								' . $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...
2996
2997
					$smcFunc['db_query']('', '
2998
						UPDATE {raw:table_name}
2999
						SET {raw:updates}',
3000
						array(
3001
							'table_name' => $table_info['Name'],
3002
							'updates' => substr($update, 0, -1),
3003
						)
3004
					);
3005
				}
3006
3007
				// Change the columns back, but with the proper character set.
3008
				$smcFunc['db_query']('', '
3009
					ALTER TABLE {raw:table_name}{raw:updates_text}',
3010
					array(
3011
						'table_name' => $table_info['Name'],
3012
						'updates_text' => substr($updates_text, 0, -1),
3013
					)
3014
				);
3015
			}
3016
3017
			// Now do the actual conversion (if still needed).
3018
			if ($charsets[$upcontext['charset_detected']] !== 'utf8')
3019
			{
3020
				if ($command_line)
3021
					echo 'Converting table ' . $table_info['Name'] . ' to UTF-8...';
3022
3023
				$smcFunc['db_query']('', '
3024
					ALTER TABLE {raw:table_name}
3025
					CONVERT TO CHARACTER SET utf8',
3026
					array(
3027
						'table_name' => $table_info['Name'],
3028
					)
3029
				);
3030
3031
				if ($command_line)
3032
					echo " done.\n";
3033
			}
3034
			// If this is XML to keep it nice for the user do one table at a time anyway!
3035
			if (isset($_GET['xml']) && $upcontext['cur_table_num'] < $upcontext['table_count'])
3036
				return upgradeExit();
3037
		}
3038
3039
		$prev_charset = empty($translation_tables[$upcontext['charset_detected']]) ? $charsets[$upcontext['charset_detected']] : $translation_tables[$upcontext['charset_detected']];
3040
3041
		$smcFunc['db_insert']('replace',
3042
			'{db_prefix}settings',
3043
			array('variable' => 'string', 'value' => 'string'),
3044
			array(array('global_character_set', 'UTF-8'), array('previousCharacterSet', $prev_charset)),
3045
			array('variable')
3046
		);
3047
3048
		// Store it in Settings.php too because it's needed before db connection.
3049
		// Hopefully this works...
3050
		require_once($sourcedir . '/Subs-Admin.php');
3051
		updateSettingsFile(array('db_character_set' => '\'utf8\''));
3052
3053
		// The conversion might have messed up some serialized strings. Fix them!
3054
		$request = $smcFunc['db_query']('', '
3055
			SELECT id_action, extra
3056
			FROM {db_prefix}log_actions
3057
			WHERE action IN ({string:remove}, {string:delete})',
3058
			array(
3059
				'remove' => 'remove',
3060
				'delete' => 'delete',
3061
			)
3062
		);
3063
		while ($row = $smcFunc['db_fetch_assoc']($request))
3064
		{
3065
			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)
3066
				$smcFunc['db_query']('', '
3067
					UPDATE {db_prefix}log_actions
3068
					SET extra = {string:extra}
3069
					WHERE id_action = {int:current_action}',
3070
					array(
3071
						'current_action' => $row['id_action'],
3072
						'extra' => $matches[1] . strlen($matches[3]) . ':"' . $matches[3] . '"' . $matches[4],
3073
					)
3074
				);
3075
		}
3076
		$smcFunc['db_free_result']($request);
3077
3078
		if ($upcontext['dropping_index'] && $command_line)
3079
		{
3080
			echo "\nYour fulltext search index was dropped to facilitate the conversion. You will need to recreate it.";
3081
			flush();
3082
		}
3083
	}
3084
	$_GET['substep'] = 0;
3085
	return false;
3086
}
3087
3088
function serialize_to_json()
3089
{
3090
	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...
3091
3092
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'serialize_json_xml' : 'serialize_json';
3093
	// First thing's first - did we already do this?
3094
	if (!empty($modSettings['json_done']))
3095
	{
3096
		if ($command_line)
3097
			return DeleteUpgrade();
3098
		else
3099
			return true;
3100
	}
3101
3102
	// Done it already - js wise?
3103
	if (!empty($_POST['json_done']))
3104
		return true;
3105
3106
	// List of tables affected by this function
3107
	// 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...
3108
	// If 3rd item in array is true, it indicates that col1 could be empty...
3109
	$tables = array(
3110
		'background_tasks' => array('id_task', 'task_data'),
3111
		'log_actions' => array('id_action', 'extra'),
3112
		'log_online' => array('session', 'url'),
3113
		'log_packages' => array('id_install', 'db_changes', 'failed_steps', 'credits'),
3114
		'log_spider_hits' => array('id_hit', 'url'),
3115
		'log_subscribed' => array('id_sublog', 'pending_details'),
3116
		'pm_rules' => array('id_rule', 'criteria', 'actions'),
3117
		'qanda' => array('id_question', 'answers'),
3118
		'subscriptions' => array('id_subscribe', 'cost'),
3119
		'user_alerts' => array('id_alert', 'extra', true),
3120
		'user_drafts' => array('id_draft', 'to_list', true),
3121
		// These last two are a bit different - we'll handle those separately
3122
		'settings' => array(),
3123
		'themes' => array()
3124
	);
3125
3126
	// Set up some context stuff...
3127
	// Because we're not using numeric indices, we need this to figure out the current table name...
3128
	$keys = array_keys($tables);
3129
3130
	$upcontext['page_title'] = $txt['converting_json'];
3131
	$upcontext['table_count'] = count($keys);
3132
	$upcontext['cur_table_num'] = $_GET['substep'];
3133
	$upcontext['cur_table_name'] = isset($keys[$_GET['substep']]) ? $keys[$_GET['substep']] : $keys[0];
3134
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3135
3136 View Code Duplication
	foreach ($keys as $id => $table)
3137
		if ($id < $_GET['substep'])
3138
			$upcontext['previous_tables'][] = $table;
3139
3140
	if ($command_line)
3141
		echo 'Converting data from serialize() to json_encode().';
3142
3143
	if (!$support_js || isset($_GET['xml']))
3144
	{
3145
		// Fix the data in each table
3146
		for ($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
3147
		{
3148
			$upcontext['cur_table_name'] = isset($keys[$substep + 1]) ? $keys[$substep + 1] : $keys[$substep];
3149
			$upcontext['cur_table_num'] = $substep + 1;
3150
3151
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3152
3153
			// Do we need to pause?
3154
			nextSubstep($substep);
3155
3156
			// Initialize a few things...
3157
			$where = '';
3158
			$vars = array();
3159
			$table = $keys[$substep];
3160
			$info = $tables[$table];
3161
3162
			// Now the fun - build our queries and all that fun stuff
3163
			if ($table == 'settings')
3164
			{
3165
				// Now a few settings...
3166
				$serialized_settings = array(
3167
					'attachment_basedirectories',
3168
					'attachmentUploadDir',
3169
					'cal_today_birthday',
3170
					'cal_today_event',
3171
					'cal_today_holiday',
3172
					'displayFields',
3173
					'last_attachments_directory',
3174
					'memberlist_cache',
3175
					'search_custom_index_config',
3176
					'spider_name_cache'
3177
				);
3178
3179
				// Loop through and fix these...
3180
				$new_settings = array();
3181
				if ($command_line)
3182
					echo "\n" . 'Fixing some settings...';
3183
3184
				foreach ($serialized_settings as $var)
3185
				{
3186
					if (isset($modSettings[$var]))
3187
					{
3188
						// Attempt to unserialize the setting
3189
						$temp = @safe_unserialize($modSettings[$var]);
3190
						if (!$temp && $command_line)
3191
							echo "\n - Failed to unserialize the '" . $var . "' setting. Skipping.";
3192
						elseif ($temp !== false)
3193
							$new_settings[$var] = json_encode($temp);
3194
					}
3195
				}
3196
3197
				// Update everything at once
3198
				if (!function_exists('cache_put_data'))
3199
					require_once($sourcedir . '/Load.php');
3200
				updateSettings($new_settings, true);
3201
3202
				if ($command_line)
3203
					echo ' done.';
3204
			}
3205
			elseif ($table == 'themes')
3206
			{
3207
				// Finally, fix the admin prefs. Unfortunately this is stored per theme, but hopefully they only have one theme installed at this point...
3208
				$query = $smcFunc['db_query']('', '
3209
					SELECT id_member, id_theme, value FROM {db_prefix}themes
3210
					WHERE variable = {string:admin_prefs}',
3211
						array(
3212
							'admin_prefs' => 'admin_preferences'
3213
						)
3214
				);
3215
3216
				if ($smcFunc['db_num_rows']($query) != 0)
3217
				{
3218
					while ($row = $smcFunc['db_fetch_assoc']($query))
3219
					{
3220
						$temp = @safe_unserialize($row['value']);
3221
3222
						if ($command_line)
3223
						{
3224
							if ($temp === false)
3225
								echo "\n" . 'Unserialize of admin_preferences for user ' . $row['id_member'] . ' failed. Skipping.';
3226
							else
3227
								echo "\n" . 'Fixing admin preferences...';
3228
						}
3229
3230
						if ($temp !== false)
3231
						{
3232
							$row['value'] = json_encode($temp);
3233
3234
							// Even though we have all values from the table, UPDATE is still faster than REPLACE
3235
							$smcFunc['db_query']('', '
3236
								UPDATE {db_prefix}themes
3237
								SET value = {string:prefs}
3238
								WHERE id_theme = {int:theme}
3239
									AND id_member = {int:member}
3240
									AND variable = {string:admin_prefs}',
3241
								array(
3242
									'prefs' => $row['value'],
3243
									'theme' => $row['id_theme'],
3244
									'member' => $row['id_member'],
3245
									'admin_prefs' => 'admin_preferences'
3246
								)
3247
							);
3248
3249
							if ($command_line)
3250
								echo ' done.';
3251
						}
3252
					}
3253
3254
					$smcFunc['db_free_result']($query);
3255
				}
3256
			}
3257
			else
3258
			{
3259
				// First item is always the key...
3260
				$key = $info[0];
3261
				unset($info[0]);
3262
3263
				// Now we know what columns we have and such...
3264
				if (count($info) == 2 && $info[2] === true)
3265
				{
3266
					$col_select = $info[1];
3267
					$where = ' WHERE ' . $info[1] . ' != {empty}';
3268
				}
3269
				else
3270
				{
3271
					$col_select = implode(', ', $info);
3272
				}
3273
3274
				$query = $smcFunc['db_query']('', '
3275
					SELECT ' . $key . ', ' . $col_select . '
3276
					FROM {db_prefix}' . $table . $where,
3277
					array()
3278
				);
3279
3280
				if ($smcFunc['db_num_rows']($query) != 0)
3281
				{
3282
					if ($command_line)
3283
					{
3284
						echo "\n" . ' +++ Fixing the "' . $table . '" table...';
3285
						flush();
3286
					}
3287
3288
					while ($row = $smcFunc['db_fetch_assoc']($query))
3289
					{
3290
						$update = '';
3291
3292
						// We already know what our key is...
3293
						foreach ($info as $col)
3294
						{
3295
							if ($col !== true && $row[$col] != '')
3296
							{
3297
								$temp = @safe_unserialize($row[$col]);
3298
3299
								if ($temp === false && $command_line)
3300
								{
3301
									echo "\nFailed to unserialize " . $row[$col] . "... Skipping\n";
3302
								}
3303
								else
3304
								{
3305
									$row[$col] = json_encode($temp);
3306
3307
									// Build our SET string and variables array
3308
									$update .= (empty($update) ? '' : ', ') . $col . ' = {string:' . $col . '}';
3309
									$vars[$col] = $row[$col];
3310
								}
3311
							}
3312
						}
3313
3314
						$vars[$key] = $row[$key];
3315
3316
						// In a few cases, we might have empty data, so don't try to update in those situations...
3317
						if (!empty($update))
3318
						{
3319
							$smcFunc['db_query']('', '
3320
								UPDATE {db_prefix}' . $table . '
3321
								SET ' . $update . '
3322
								WHERE ' . $key . ' = {' . ($key == 'session' ? 'string' : 'int') . ':' . $key . '}',
3323
								$vars
3324
							);
3325
						}
3326
					}
3327
3328
					if ($command_line)
3329
						echo ' done.';
3330
3331
					// Free up some memory...
3332
					$smcFunc['db_free_result']($query);
3333
				}
3334
			}
3335
			// If this is XML to keep it nice for the user do one table at a time anyway!
3336
			if (isset($_GET['xml']))
3337
				return upgradeExit();
3338
		}
3339
3340
		if ($command_line)
3341
		{
3342
			echo "\n" . 'Successful.' . "\n";
3343
			flush();
3344
		}
3345
		$upcontext['step_progress'] = 100;
3346
3347
		// Last but not least, insert a dummy setting so we don't have to do this again in the future...
3348
		updateSettings(array('json_done' => true));
3349
3350
		$_GET['substep'] = 0;
3351
		// Make sure we move on!
3352
		if ($command_line)
3353
			return DeleteUpgrade();
3354
3355
		return true;
3356
	}
3357
3358
	// If this fails we just move on to deleting the upgrade anyway...
3359
	$_GET['substep'] = 0;
3360
	return false;
3361
}
3362
3363
/**
3364
 * As of 2.1, we want to store db_last_error.php in the cache
3365
 * To make that happen, Settings.php needs to ensure the $cachedir path is correct before trying to write to db_last_error.php
3366
 */
3367
function move_db_last_error_to_cachedir()
3368
{
3369
	$settings = file_get_contents(dirname(__FILE__) . '/Settings.php');
3370
3371
	$regex = <<<'EOT'
3372
(\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';
3373
EOT;
3374
3375
	$replacement = <<<'EOT'
3376
# Make sure the paths are correct... at least try to fix them.
3377
if (!file_exists($boarddir) && file_exists(dirname(__FILE__) . '/agreement.txt'))
3378
	$boarddir = dirname(__FILE__);
3379
if (!file_exists($sourcedir) && file_exists($boarddir . '/Sources'))
3380
	$sourcedir = $boarddir . '/Sources';
3381
if (!file_exists($cachedir) && file_exists($boarddir . '/cache'))
3382
	$cachedir = $boarddir . '/cache';
3383
3384
3385
EOT;
3386
3387
	if (preg_match('~' . $regex . '~', $settings) && preg_match('~(#+\s*Error-Catching\s*#+)~', $settings))
3388
	{
3389
		$settings = preg_replace('~' . $regex . '~', '', $settings);
3390
		$settings = preg_replace('~(#+\s*Error-Catching\s*#+)~', $replacement . '$1', $settings);
3391
		$settings = preg_replace('~dirname(__FILE__) . \'/db_last_error.php\'~', '(isset($cachedir) ? $cachedir : dirname(__FILE__)) . \'/db_last_error.php\'', $settings);
3392
3393
		// Blank out the file - done to fix a oddity with some servers.
3394
		file_put_contents(dirname(__FILE__) . '/Settings.php', '');
3395
3396
		file_put_contents(dirname(__FILE__) . '/Settings.php', $settings);
3397
	}
3398
}
3399
3400
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3401
                        Templates are below this point
3402
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
3403
3404
// This is what is displayed if there's any chmod to be done. If not it returns nothing...
3405
function template_chmod()
3406
{
3407
	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...
3408
3409
	// Don't call me twice!
3410
	if (!empty($upcontext['chmod_called']))
3411
		return;
3412
3413
	$upcontext['chmod_called'] = true;
3414
3415
	// Nothing?
3416
	if (empty($upcontext['chmod']['files']) && empty($upcontext['chmod']['ftp_error']))
3417
		return;
3418
3419
	// Was it a problem with Windows?
3420
	if (!empty($upcontext['chmod']['ftp_error']) && $upcontext['chmod']['ftp_error'] == 'total_mess')
3421
	{
3422
		echo '
3423
		<div class="error">
3424
			<p>The following files need to be writable to continue the upgrade. Please ensure the Windows permissions are correctly set to allow this:</p>
3425
			<ul class="error_content">
3426
				<li>' . implode('</li>
3427
				<li>', $upcontext['chmod']['files']) . '</li>
3428
			</ul>
3429
		</div>';
3430
3431
		return false;
3432
	}
3433
3434
	echo '
3435
		<div class="panel">
3436
			<h2>', $txt['upgrade_ftp_login'], '</h2>
3437
			<h3>', $txt['upgrade_ftp_perms'], '</h3>
3438
			<script>
3439
				function warning_popup()
3440
				{
3441
					popup = window.open(\'\',\'popup\',\'height=150,width=400,scrollbars=yes\');
3442
					var content = popup.document;
3443
					content.write(\'<!DOCTYPE html>\n\');
3444
					content.write(\'<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>\n\t<head>\n\t\t<meta name="robots" content="noindex">\n\t\t\');
3445
					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\');
3446
					content.write(\'<div class="windowbg description">\n\t\t\t<h4>', $txt['upgrade_ftp_files'], '</h4>\n\t\t\t\');
3447
					content.write(\'<p>', implode('<br>\n\t\t\t', $upcontext['chmod']['files']), '</p>\n\t\t\t\');';
3448
3449
	if (isset($upcontext['systemos']) && $upcontext['systemos'] == 'linux')
3450
		echo '
3451
					content.write(\'<hr>\n\t\t\t\');
3452
					content.write(\'<p>', $txt['upgrade_ftp_shell'], '</p>\n\t\t\t\');
3453
					content.write(\'<tt># chmod a+w ', implode(' ', $upcontext['chmod']['files']), '</tt>\n\t\t\t\');';
3454
3455
	echo '
3456
					content.write(\'<a href="javascript:self.close();">close</a>\n\t\t</div>\n\t</body>\n</html>\');
3457
					content.close();
3458
				}
3459
			</script>';
3460
3461
	if (!empty($upcontext['chmod']['ftp_error']))
3462
		echo '
3463
			<div class="error_message red">
3464
				<p>', $txt['upgrade_ftp_error'], '<p>
3465
				<code>', $upcontext['chmod']['ftp_error'], '</code>
3466
			</div>';
3467
3468
	if (empty($upcontext['chmod_in_form']))
3469
		echo '
3470
			<form action="', $upcontext['form_url'], '" method="post">';
3471
3472
	echo '
3473
				<dl class="settings">
3474
					<dt>
3475
						<label for="ftp_server">', $txt['ftp_server'], ':</label>
3476
					</dt>
3477
					<dd>
3478
						<div class="floatright">
3479
							<label for="ftp_port" class="textbox"><strong>', $txt['ftp_port'], ':</strong></label>
3480
							<input type="text" size="3" name="ftp_port" id="ftp_port" value="', isset($upcontext['chmod']['port']) ? $upcontext['chmod']['port'] : '21', '">
3481
						</div>
3482
						<input type="text" size="30" name="ftp_server" id="ftp_server" value="', isset($upcontext['chmod']['server']) ? $upcontext['chmod']['server'] : 'localhost', '">
3483
						<div class="smalltext">', $txt['ftp_server_info'], '</div>
3484
					</dd>
3485
					<dt>
3486
						<label for="ftp_username">', $txt['ftp_username'], ':</label>
3487
					</dt>
3488
					<dd>
3489
						<input type="text" size="30" name="ftp_username" id="ftp_username" value="', isset($upcontext['chmod']['username']) ? $upcontext['chmod']['username'] : '', '">
3490
						<div class="smalltext">', $txt['ftp_username_info'], '</div>
3491
					</dd>
3492
					<dt>
3493
						<label for="ftp_password">', $txt['ftp_password'], ':</label>
3494
					</dt>
3495
					<dd>
3496
						<input type="password" size="30" name="ftp_password" id="ftp_password">
3497
						<div class="smalltext">', $txt['ftp_password_info'], '</div>
3498
					</dd>
3499
					<dt>
3500
						<label for="ftp_path">', $txt['ftp_path'], ':</label>
3501
					</dt>
3502
					<dd>
3503
						<input type="text" size="30" name="ftp_path" id="ftp_path" value="', isset($upcontext['chmod']['path']) ? $upcontext['chmod']['path'] : '', '">
3504
						<div class="smalltext">', !empty($upcontext['chmod']['path']) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'], '</div>
3505
					</dd>
3506
				</dl>
3507
3508
				<div class="righttext buttons">
3509
					<input type="submit" value="', $txt['ftp_connect'], '" class="button">
3510
				</div>';
3511
3512
	if (empty($upcontext['chmod_in_form']))
3513
		echo '
3514
			</form>';
3515
3516
	echo '
3517
		</div><!-- .panel -->';
3518
}
3519
3520
function template_upgrade_above()
3521
{
3522
	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...
3523
3524
	echo '<!DOCTYPE html>
3525
<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>
3526
<head>
3527
	<meta charset="', isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8', '">
3528
	<meta name="robots" content="noindex">
3529
	<title>', $txt['upgrade_upgrade_utility'], '</title>
3530
	<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/index.css?alp21">
3531
	<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/install.css?alp21">
3532
	', $txt['lang_rtl'] == true ? '<link rel="stylesheet" href="' . $settings['default_theme_url'] . '/css/rtl.css?alp21">' : '', '
3533
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
3534
	<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
3535
	<script>
3536
		var smf_scripturl = \'', $upgradeurl, '\';
3537
		var smf_charset = \'', (empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'UTF-8' : $txt['lang_character_set']) : $modSettings['global_character_set']), '\';
3538
		var startPercent = ', $upcontext['overall_percent'], ';
3539
3540
		// This function dynamically updates the step progress bar - and overall one as required.
3541
		function updateStepProgress(current, max, overall_weight)
3542
		{
3543
			// What out the actual percent.
3544
			var width = parseInt((current / max) * 100);
3545
			if (document.getElementById(\'step_progress\'))
3546
			{
3547
				document.getElementById(\'step_progress\').style.width = width + "%";
3548
				setInnerHTML(document.getElementById(\'step_text\'), width + "%");
3549
			}
3550
			if (overall_weight && document.getElementById(\'overall_progress\'))
3551
			{
3552
				overall_width = parseInt(startPercent + width * (overall_weight / 100));
3553
				document.getElementById(\'overall_progress\').style.width = overall_width + "%";
3554
				setInnerHTML(document.getElementById(\'overall_text\'), overall_width + "%");
3555
			}
3556
		}
3557
	</script>
3558
</head>
3559
<body>
3560
	<div id="footerfix">
3561
	<div id="header">
3562
		<h1 class="forumtitle">', $txt['upgrade_upgrade_utility'], '</h1>
3563
		<img id="smflogo" src="', $settings['default_theme_url'], '/images/smflogo.svg" alt="Simple Machines Forum" title="Simple Machines Forum">
3564
	</div>
3565
	<div id="wrapper">
3566
		<div id="upper_section">
3567
			<div id="inner_section">
3568
				<div id="inner_wrap">
3569
				</div>
3570
			</div>
3571
		</div>
3572
		<div id="content_section">
3573
			<div id="main_content_section">
3574
				<div id="main_steps">
3575
					<h2>', $txt['upgrade_progress'], '</h2>
3576
					<ul>';
3577
3578 View Code Duplication
	foreach ($upcontext['steps'] as $num => $step)
3579
		echo '
3580
						<li class="', $num < $upcontext['current_step'] ? 'stepdone' : ($num == $upcontext['current_step'] ? 'stepcurrent' : 'stepwaiting'), '">', $txt['upgrade_step'], ' ', $step[0], ': ', $step[1], '</li>';
3581
3582
	echo '
3583
					</ul>
3584
				</div><!-- #main_steps -->
3585
				
3586
				<div id="install_progress">
3587
					<div id="progress_bar" class="progress_bar progress_green">
3588
						<h3>', $txt['upgrade_overall_progress'], '</h3>
3589
						<div id="overall_progress" class="bar" style="width: ', $upcontext['overall_percent'], '%;"></div>
3590
						<span id="overall_text">', $upcontext['overall_percent'], '%</span>
3591
					</div>';
3592
3593
	if (isset($upcontext['step_progress']))
3594
		echo '
3595
					<div id="progress_bar_step" class="progress_bar progress_yellow">
3596
						<h3>', $txt['upgrade_step_progress'], '</h3>
3597
						<div id="step_progress" class="bar" style="width: ', $upcontext['step_progress'], '%;"></div>
3598
						<span id="step_text">', $upcontext['step_progress'], '%</span>
3599
					</div>';
3600
3601
	echo '
3602
					<h3>', isset($upcontext['substep_progress_name']) ? trim(strtr($upcontext['substep_progress_name'], array('.' => ''))) : '', '</h3>
3603
					<div id="substep_bar_div" class="progress_bar" style="display: ', isset($upcontext['substep_progress']) ? '' : 'none', ';">
3604
						<div id="substep_progress" class="bar" style="width: ', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%;"></div>
3605
						<span>', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : '', '%</span>
3606
					</div>';
3607
3608
	// How long have we been running this?
3609
	$elapsed = time() - $upcontext['started'];
3610
	$mins = (int) ($elapsed / 60);
3611
	$seconds = $elapsed - $mins * 60;
3612
	echo '
3613
					<div class="smalltext time_elapsed">
3614
						', $txt['upgrade_time_elapsed'], ':
3615
						<span id="mins_elapsed">', $mins, '</span> ', $txt['upgrade_time_mins'], ', <span id="secs_elapsed">', $seconds, '</span> ', $txt['upgrade_time_secs'], '.
3616
					</div>';
3617
	echo '
3618
				</div><!-- #install_progress -->
3619
			</div><!-- #main_content_section -->
3620
		</div><!-- #content_section -->
3621
		<div id="main_screen" class="clear">
3622
			<h2>', $upcontext['page_title'], '</h2>
3623
			<div class="panel">';
3624
}
3625
3626
function template_upgrade_below()
3627
{
3628
	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...
3629
3630
	if (!empty($upcontext['pause']))
3631
		echo '
3632
					<em>', $txt['upgrade_incomplete'], '.</em><br>
3633
3634
					<h2 style="margin-top: 2ex;">', $txt['upgrade_not_quite_done'], '</h2>
3635
					<h3>
3636
						', $txt['upgrade_paused_overload'], '
3637
					</h3>';
3638
3639
	if (!empty($upcontext['custom_warning']))
3640
		echo '
3641
					<div class="errorbox">
3642
						<h3>', $txt['upgrade_note'], '</h3>
3643
						', $upcontext['custom_warning'], '
3644
					</div>';
3645
3646
	echo '
3647
					<div class="righttext" style="margin: 1ex;">';
3648
3649
	if (!empty($upcontext['continue']))
3650
		echo '
3651
						<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '"', $upcontext['continue'] == 2 ? ' disabled' : '', ' class="button">';
3652
	if (!empty($upcontext['skip']))
3653
		echo '
3654
						<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="dontSubmit = true; document.getElementById(\'contbutt\').disabled = \'disabled\'; return true;" class="button">';
3655
3656
	echo '
3657
					</div>
3658
				</form>
3659
			</div><!-- .panel -->
3660
		</div><!-- #main_screen -->
3661
	</div><!-- #wrapper -->
3662
	</div><!-- #footerfix -->
3663
	<div id="footer">
3664
		<ul>
3665
			<li class="copyright"><a href="https://www.simplemachines.org/" title="Simple Machines Forum" target="_blank" rel="noopener">SMF &copy; 2018, Simple Machines</a></li>
3666
		</ul>
3667
	</div>';
3668
3669
	// Are we on a pause?
3670
	if (!empty($upcontext['pause']))
3671
	{
3672
		echo '
3673
	<script>
3674
		window.onload = doAutoSubmit;
3675
		var countdown = 3;
3676
		var dontSubmit = false;
3677
3678
		function doAutoSubmit()
3679
		{
3680
			if (countdown == 0 && !dontSubmit)
3681
				document.upform.submit();
3682
			else if (countdown == -1)
3683
				return;
3684
3685
			document.getElementById(\'contbutt\').value = "', $txt['upgrade_continue'], ' (" + countdown + ")";
3686
			countdown--;
3687
3688
			setTimeout("doAutoSubmit();", 1000);
3689
		}
3690
	</script>';
3691
	}
3692
3693
	echo '
3694
</body>
3695
</html>';
3696
}
3697
3698
function template_xml_above()
3699
{
3700
	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...
3701
3702
	echo '<', '?xml version="1.0" encoding="UTF-8"?', '>
3703
	<smf>';
3704
3705
	if (!empty($upcontext['get_data']))
3706
		foreach ($upcontext['get_data'] as $k => $v)
3707
			echo '
3708
		<get key="', $k, '">', $v, '</get>';
3709
}
3710
3711
function template_xml_below()
3712
{
3713
	echo '
3714
	</smf>';
3715
}
3716
3717
function template_error_message()
3718
{
3719
	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...
3720
3721
	echo '
3722
	<div class="error_message red">
3723
		', $upcontext['error_msg'], '
3724
		<br>
3725
		<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 3725

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...
3726
	</div>';
3727
}
3728
3729
function template_welcome_message()
0 ignored issues
show
Best Practice introduced by
The function template_welcome_message() has been defined more than once; this definition is ignored, only the first definition in other/install.php (L2025-2071) is considered.

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

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

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

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

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