Completed
Push — release-2.1 ( c3533f...562b90 )
by Jeremy
07:59
created

upgrade.php ➔ template_upgrade_complete()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

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

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

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

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

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

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

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

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

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

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

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

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

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

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

1 path for user data to reach this point

  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 321
  2. $upcontext is assigned
    in other/upgrade.php on line 322
  3. $upcontext is assigned
    in other/upgrade.php on line 323
  4. $upcontext is assigned
    in other/upgrade.php on line 336

Preventing Cross-Site-Scripting Attacks

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

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

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

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

General Strategies to prevent injection

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

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

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

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

3 paths for user data to reach this point

  1. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 321
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 321
  2. $upcontext is assigned
    in other/upgrade.php on line 322
  3. $upcontext is assigned
    in other/upgrade.php on line 323
  4. $upcontext is assigned
    in other/upgrade.php on line 336
  5. $upcontext is assigned
    in other/upgrade.php on line 372
  6. $upcontext is assigned
    in other/upgrade.php on line 373
  2. Path: Read from $_GET, and $v is assigned in other/upgrade.php on line 359
  1. Read from $_GET, and $v is assigned
    in other/upgrade.php on line 359
  2. $upcontext is assigned
    in other/upgrade.php on line 363
  3. $upcontext is assigned
    in other/upgrade.php on line 372
  4. $upcontext is assigned
    in other/upgrade.php on line 373
  3. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 373
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 373

General Strategies to prevent injection

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

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

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

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

3 paths for user data to reach this point

  1. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 321
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 321
  2. $upcontext is assigned
    in other/upgrade.php on line 322
  3. $upcontext is assigned
    in other/upgrade.php on line 323
  4. $upcontext is assigned
    in other/upgrade.php on line 336
  5. $upcontext is assigned
    in other/upgrade.php on line 372
  6. $upcontext is assigned
    in other/upgrade.php on line 373
  2. Path: Read from $_GET, and $v is assigned in other/upgrade.php on line 359
  1. Read from $_GET, and $v is assigned
    in other/upgrade.php on line 359
  2. $upcontext is assigned
    in other/upgrade.php on line 363
  3. $upcontext is assigned
    in other/upgrade.php on line 372
  4. $upcontext is assigned
    in other/upgrade.php on line 373
  3. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 373
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 373

Preventing Cross-Site-Scripting Attacks

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

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

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

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

General Strategies to prevent injection

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

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

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

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

3 paths for user data to reach this point

  1. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 321
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 321
  2. $upcontext is assigned
    in other/upgrade.php on line 322
  3. $upcontext is assigned
    in other/upgrade.php on line 323
  4. $upcontext is assigned
    in other/upgrade.php on line 336
  2. Path: Read from $_GET, and $v is assigned in other/upgrade.php on line 359
  1. Read from $_GET, and $v is assigned
    in other/upgrade.php on line 359
  2. $upcontext is assigned
    in other/upgrade.php on line 363
  3. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 373
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 373

Preventing Cross-Site-Scripting Attacks

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

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

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

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

General Strategies to prevent injection

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

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

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

$sanitized = (integer) $tainted;
Loading history...
389
390
		// Show the footer.
391
		if (!isset($_GET['xml']))
392
			template_upgrade_below();
393
		else
394
			template_xml_below();
395
	}
396
397
	// Show the upgrade time for CLI when we are completely done, if in debug mode.
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 = '';
0 ignored issues
show
Unused Code introduced by
$totalTime 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...
406
		if ($hours > 0)
407
			echo "\n" . '', sprintf($txt['upgrade_completed_time_hms'], $hours, $minutes, $seconds), '' . "\n";
408
		elseif ($minutes > 0)
409
			echo "\n" . '', sprintf($txt['upgrade_completed_time_ms'], $minutes, $seconds), '' . "\n";
410
		elseif ($seconds > 0)
411
			echo "\n" . '', sprintf($txt['upgrade_completed_time_s'], $seconds), '' . "\n";
412
	}
413
414
	// Bang - gone!
415
	die();
416
}
417
418
// Load the list of language files, and the current language file.
419
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...
420
{
421
	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...
422
423
	$incontext['detected_languages'] = array();
424
425
	// Make sure the languages directory actually exists.
426 View Code Duplication
	if (file_exists(dirname(__FILE__) . '/Themes/default/languages'))
427
	{
428
		// Find all the "Install" language files in the directory.
429
		$dir = dir(dirname(__FILE__) . '/Themes/default/languages');
430
		while ($entry = $dir->read())
431
		{
432
			if (substr($entry, 0, 8) == 'Install.' && substr($entry, -4) == '.php')
433
				$incontext['detected_languages'][$entry] = ucfirst(substr($entry, 8, strlen($entry) - 12));
434
		}
435
		$dir->close();
436
	}
437
438
	// Didn't find any, show an error message!
439 View Code Duplication
	if (empty($incontext['detected_languages']))
440
	{
441
		// Let's not cache this message, eh?
442
		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
443
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
444
		header('Cache-Control: no-cache');
445
446
		echo '<!DOCTYPE html>
447
			<html>
448
				<head>
449
					<title>SMF Upgrader: Error!</title>
450
						<style>
451
							body {
452
								font-family: sans-serif;
453
								max-width: 700px; }
454
455
								h1 {
456
									font-size: 14pt; }
457
458
								.directory {
459
									margin: 0.3em;
460
									font-family: monospace;
461
									font-weight: bold; }
462
						</style>
463
				</head>
464
				<body>
465
					<h1>A critical error has occurred.</h1>
466
						<p>This upgrader was unable to find the upgrader\'s language file or files.  They should be found under:</p>
467
						<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 467

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

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...
528
529
	// Exit - saving status as we go.
530
	upgradeExit(true);
531
}
532
533
// Load all essential data and connect to the DB as this is pre SSI.php
534
function loadEssentialData()
535
{
536
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection, $db_prefix, $db_character_set, $db_type, $db_port;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

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

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...
686
		exit;
687
	}
688
689
	// Something is causing this to happen, and it's annoying.  Stop it.
690
	$temp = 'upgrade_php?step';
691
	while (strlen($temp) > 4)
692
	{
693
		if (isset($_GET[$temp]))
694
			unset($_GET[$temp]);
695
		$temp = substr($temp, 1);
696
	}
697
698
	// Force a step, defaulting to 0.
699
	$_GET['step'] = (int) @$_GET['step'];
700
	$_GET['substep'] = (int) @$_GET['substep'];
701
}
702
703
// Step 0 - Let's welcome them in and ask them to login!
704
function WelcomeLogin()
705
{
706
	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...
707
	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...
708
709
	// We global $txt here so that the language files can add to them. This variable is NOT unused.
710
	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...
711
712
	$upcontext['sub_template'] = 'welcome_message';
713
714
	// Check for some key files - one template, one language, and a new and an old source file.
715
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
716
		&& @file_exists($sourcedir . '/QueryString.php')
717
		&& @file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')
718
		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
719
720
	// Need legacy scripts?
721 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.1)
722
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
723 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.0)
724
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-1.sql');
725 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 1.1)
726
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
727
728
	// We don't need "-utf8" files anymore...
729
	$upcontext['language'] = str_ireplace('-utf8', '', $upcontext['language']);
730
731
	// This needs to exist!
732
	if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
733
		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>]');
734
	else
735
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
736
737
	if (!$check)
738
		// 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.
739
		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.');
740
741
	// Do they meet the install requirements?
742
	if (!php_version_check())
743
		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.');
744
745
	if (!db_version_check())
746
		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.');
747
748
	// Do some checks to make sure they have proper privileges
749
	db_extend('packages');
750
751
	// CREATE
752
	$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');
753
754
	// ALTER
755
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
756
757
	// DROP
758
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
759
760
	// Sorry... we need CREATE, ALTER and DROP
761 View Code Duplication
	if (!$create || !$alter || !$drop)
762
		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.');
763
764
	// Do a quick version spot check.
765
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
766
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
767
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
768
		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.');
769
770
	// What absolutely needs to be writable?
771
	$writable_files = array(
772
		$boarddir . '/Settings.php',
773
		$boarddir . '/Settings_bak.php',
774
	);
775
776
	// Only check for minified writable files if we have it enabled or not set.
777
	if (!empty($modSettings['minimize_files']) || !isset($modSettings['minimize_files']))
778
		$writable_files += array(
779
			$modSettings['theme_dir'] . '/css/minified.css',
780
			$modSettings['theme_dir'] . '/scripts/minified.js',
781
			$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
782
		);
783
784
	// Do we need to add this setting?
785
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
786
787
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] . '/custom_avatar';
788
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
789
790
	// This little fellow has to cooperate...
791
	quickFileWritable($custom_av_dir);
792
793
	// Are we good now?
794
	if (!is_writable($custom_av_dir))
795
		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));
796
	elseif ($need_settings_update)
797
	{
798
		if (!function_exists('cache_put_data'))
799
			require_once($sourcedir . '/Load.php');
800
801
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
802
		updateSettings(array('custom_avatar_url' => $custom_av_url));
803
	}
804
805
	require_once($sourcedir . '/Security.php');
806
807
	// Check the cache directory.
808
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
809
	if (!file_exists($cachedir_temp))
810
		@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...
811
812
	if (!file_exists($cachedir_temp))
813
		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.');
814
815
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
816
		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>.');
817
	elseif (!isset($_GET['skiplang']))
818
	{
819
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
820
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
821
822
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
823
			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>]');
824
	}
825
826
	if (!makeFilesWritable($writable_files))
827
		return false;
828
829
	// Check agreement.txt. (it may not exist, in which case $boarddir must be writable.)
830 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
831
		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.');
832
833
	// Upgrade the agreement.
834
	elseif (isset($modSettings['agreement']))
835
	{
836
		$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...
837
		fwrite($fp, $modSettings['agreement']);
838
		fclose($fp);
839
	}
840
841
	// We're going to check that their board dir setting is right in case they've been moving stuff around.
842
	if (strtr($boarddir, array('/' => '', '\\' => '')) != strtr(dirname(__FILE__), array('/' => '', '\\' => '')))
843
		$upcontext['warning'] = '
844
			'. sprintf($txt['upgrade_boarddir_settings'], $boarddir, dirname(__FILE__)) .'<br>
845
			<ul>
846
				<li>'. $txt['upgrade_boarddir'] .'  ' . $boarddir . '</li>
847
				<li>'. $txt['upgrade_sourcedir'] .'  ' . $boarddir . '</li>
848
				<li>'. $txt['upgrade_cachedir'] .'  ' . $cachedir_temp . '</li>
849
			</ul>
850
			'. $txt['upgrade_incorrect_settings'] .'';
851
852
	// Confirm mbstring is loaded...
853
	if (!extension_loaded('mbstring'))
854
		return throw_error($txt['install_no_mbstring']);
855
856
	// Check for https stream support.
857
	$supported_streams = stream_get_wrappers();
858
	if (!in_array('https', $supported_streams))
859
		$upcontext['custom_warning'] = $txt['install_no_https'];
860
861
	// Either we're logged in or we're going to present the login.
862
	if (checkLogin())
863
		return true;
864
865
	$upcontext += createToken('login');
866
867
	return false;
868
}
869
870
// Step 0.5: Does the login work?
871
function checkLogin()
872
{
873
	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...
874
	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...
875
876
	// Don't bother if the security is disabled.
877
	if ($disable_security)
878
		return true;
879
880
	// Are we trying to login?
881
	if (isset($_POST['contbutt']) && (!empty($_POST['user'])))
882
	{
883
		// If we've disabled security pick a suitable name!
884
		if (empty($_POST['user']))
885
			$_POST['user'] = 'Administrator';
886
887
		// Before 2.0 these column names were different!
888
		$oldDB = false;
889
		if (empty($db_type) || $db_type == 'mysql')
890
		{
891
			$request = $smcFunc['db_query']('', '
892
				SHOW COLUMNS
893
				FROM {db_prefix}members
894
				LIKE {string:member_name}',
895
				array(
896
					'member_name' => 'memberName',
897
					'db_error_skip' => true,
898
				)
899
			);
900
			if ($smcFunc['db_num_rows']($request) != 0)
901
				$oldDB = true;
902
			$smcFunc['db_free_result']($request);
903
		}
904
905
		// Get what we believe to be their details.
906
		if (!$disable_security)
907
		{
908
			if ($oldDB)
909
				$request = $smcFunc['db_query']('', '
910
					SELECT id_member, memberName AS member_name, passwd, id_group,
911
					additionalGroups AS additional_groups, lngfile
912
					FROM {db_prefix}members
913
					WHERE memberName = {string:member_name}',
914
					array(
915
						'member_name' => $_POST['user'],
916
						'db_error_skip' => true,
917
					)
918
				);
919
			else
920
				$request = $smcFunc['db_query']('', '
921
					SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile
922
					FROM {db_prefix}members
923
					WHERE member_name = {string:member_name}',
924
					array(
925
						'member_name' => $_POST['user'],
926
						'db_error_skip' => true,
927
					)
928
				);
929
			if ($smcFunc['db_num_rows']($request) != 0)
930
			{
931
				list ($id_member, $name, $password, $id_group, $addGroups, $user_language) = $smcFunc['db_fetch_row']($request);
932
933
				$groups = explode(',', $addGroups);
934
				$groups[] = $id_group;
935
936
				foreach ($groups as $k => $v)
937
					$groups[$k] = (int) $v;
938
939
				$sha_passwd = sha1(strtolower($name) . un_htmlspecialchars($_REQUEST['passwrd']));
940
941
				// We don't use "-utf8" anymore...
942
				$user_language = str_ireplace('-utf8', '', $user_language);
943
			}
944
			else
945
				$upcontext['username_incorrect'] = true;
946
947
			$smcFunc['db_free_result']($request);
948
		}
949
		$upcontext['username'] = $_POST['user'];
950
951
		// Track whether javascript works!
952
		if (!empty($_POST['js_works']))
953
		{
954
			$upcontext['upgrade_status']['js'] = 1;
955
			$support_js = 1;
956
		}
957
		else
958
			$support_js = 0;
959
960
		// Note down the version we are coming from.
961
		if (!empty($modSettings['smfVersion']) && empty($upcontext['user']['version']))
962
			$upcontext['user']['version'] = $modSettings['smfVersion'];
963
964
		// Didn't get anywhere?
965
		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']))
966
		{
967
			// MD5?
968
			$md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user']));
969
			if ($md5pass != $password)
970
			{
971
				$upcontext['password_failed'] = true;
972
				// Disable the hashing this time.
973
				$upcontext['disable_login_hashing'] = true;
974
			}
975
		}
976
977
		if ((empty($upcontext['password_failed']) && !empty($name)) || $disable_security)
978
		{
979
			// Set the password.
980
			if (!$disable_security)
981
			{
982
				// Do we actually have permission?
983
				if (!in_array(1, $groups))
984
				{
985
					$request = $smcFunc['db_query']('', '
986
						SELECT permission
987
						FROM {db_prefix}permissions
988
						WHERE id_group IN ({array_int:groups})
989
							AND permission = {string:admin_forum}',
990
						array(
991
							'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...
992
							'admin_forum' => 'admin_forum',
993
							'db_error_skip' => true,
994
						)
995
					);
996
					if ($smcFunc['db_num_rows']($request) == 0)
997
						return throw_error('You need to be an admin to perform an upgrade!');
998
					$smcFunc['db_free_result']($request);
999
				}
1000
1001
				$upcontext['user']['id'] = $id_member;
1002
				$upcontext['user']['name'] = $name;
1003
			}
1004
			else
1005
			{
1006
				$upcontext['user']['id'] = 1;
1007
				$upcontext['user']['name'] = 'Administrator';
1008
			}
1009
			$upcontext['user']['pass'] = mt_rand(0, 60000);
1010
			// This basically is used to match the GET variables to Settings.php.
1011
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
1012
1013
			// Set the language to that of the user?
1014
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
1015
			{
1016
				$user_language = basename($user_language, '.lng');
1017
				$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
1018
				preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
1019
1020
				if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
1021
					$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'] . '.';
1022
				elseif (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . basename($user_language, '.lng') . '.php'))
1023
					$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'] . '.';
1024
				else
1025
				{
1026
					// Set this as the new language.
1027
					$upcontext['language'] = $user_language;
1028
					$upcontext['upgrade_status']['lang'] = $upcontext['language'];
1029
1030
					// Include the file.
1031
					require_once($modSettings['theme_dir'] . '/languages/Install.' . $user_language . '.php');
1032
				}
1033
			}
1034
1035
			// If we're resuming set the step and substep to be correct.
1036
			if (isset($_POST['cont']))
1037
			{
1038
				$upcontext['current_step'] = $upcontext['user']['step'];
1039
				$_GET['substep'] = $upcontext['user']['substep'];
1040
			}
1041
1042
			return true;
1043
		}
1044
	}
1045
1046
	return false;
1047
}
1048
1049
// Step 1: Do the maintenance and backup.
1050
function UpgradeOptions()
1051
{
1052
	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...
1053
	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...
1054
1055
	$upcontext['sub_template'] = 'upgrade_options';
1056
	$upcontext['page_title'] = $txt['upgrade_options'];
1057
1058
	db_extend('packages');
1059
	$upcontext['karma_installed'] = array('good' => false, 'bad' => false);
1060
	$member_columns = $smcFunc['db_list_columns']('{db_prefix}members');
1061
1062
	$upcontext['karma_installed']['good'] = in_array('karma_good', $member_columns);
1063
	$upcontext['karma_installed']['bad'] = in_array('karma_bad', $member_columns);
1064
1065
	unset($member_columns);
1066
1067
	// If we've not submitted then we're done.
1068
	if (empty($_POST['upcont']))
1069
		return false;
1070
1071
	// Firstly, if they're enabling SM stat collection just do it.
1072
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']))
1073
	{
1074
		$upcontext['allow_sm_stats'] = true;
1075
1076
		// Don't register if we still have a key.
1077
		if (empty($modSettings['sm_stats_key']))
1078
		{
1079
			// Attempt to register the site etc.
1080
			$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...
1081 View Code Duplication
			if ($fp)
1082
			{
1083
				$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
1084
				$out .= 'Host: www.simplemachines.org' . "\r\n";
1085
				$out .= 'Connection: Close' . "\r\n\r\n";
1086
				fwrite($fp, $out);
1087
1088
				$return_data = '';
1089
				while (!feof($fp))
1090
					$return_data .= fgets($fp, 128);
1091
1092
				fclose($fp);
1093
1094
				// Get the unique site ID.
1095
				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...
1096
1097
				if (!empty($ID[1]))
1098
					$smcFunc['db_insert']('replace',
1099
						$db_prefix . 'settings',
1100
						array('variable' => 'string', 'value' => 'string'),
1101
						array(
1102
							array('sm_stats_key', $ID[1]),
1103
							array('enable_sm_stats', 1),
1104
						),
1105
						array('variable')
1106
					);
1107
			}
1108
		}
1109
		else
1110
		{
1111
			$smcFunc['db_insert']('replace',
1112
				$db_prefix . 'settings',
1113
				array('variable' => 'string', 'value' => 'string'),
1114
				array('enable_sm_stats', 1),
1115
				array('variable')
1116
			);
1117
		}
1118
	}
1119
	// Don't remove stat collection unless we unchecked the box for real, not from the loop.
1120 View Code Duplication
	elseif (empty($_POST['stats']) && empty($upcontext['allow_sm_stats']))
1121
		$smcFunc['db_query']('', '
1122
			DELETE FROM {db_prefix}settings
1123
			WHERE variable = {string:enable_sm_stats}',
1124
			array(
1125
				'enable_sm_stats' => 'enable_sm_stats',
1126
				'db_error_skip' => true,
1127
			)
1128
		);
1129
1130
	// Deleting old karma stuff?
1131
	if (!empty($_POST['delete_karma']))
1132
	{
1133
		// Delete old settings vars.
1134
		$smcFunc['db_query']('', '
1135
			DELETE FROM {db_prefix}settings
1136
			WHERE variable IN ({array_string:karma_vars})',
1137
			array(
1138
				'karma_vars' => array('karmaMode', 'karmaTimeRestrictAdmins', 'karmaWaitTime', 'karmaMinPosts', 'karmaLabel', 'karmaSmiteLabel', 'karmaApplaudLabel'),
1139
			)
1140
		);
1141
1142
		// Cleaning up old karma member settings.
1143
		if ($upcontext['karma_installed']['good'])
1144
			$smcFunc['db_query']('', '
1145
				ALTER TABLE {db_prefix}members
1146
				DROP karma_good',
1147
				array()
1148
			);
1149
1150
		// Does karma bad was enable?
1151
		if ($upcontext['karma_installed']['bad'])
1152
			$smcFunc['db_query']('', '
1153
				ALTER TABLE {db_prefix}members
1154
				DROP karma_bad',
1155
				array()
1156
			);
1157
1158
		// Cleaning up old karma permissions.
1159
		$smcFunc['db_query']('', '
1160
			DELETE FROM {db_prefix}permissions
1161
			WHERE permission = {string:karma_vars}',
1162
			array(
1163
				'karma_vars' => 'karma_edit',
1164
			)
1165
		);
1166
		// Cleaning up old log_karma table
1167
		$smcFunc['db_query']('', '
1168
			DROP TABLE IF EXISTS {db_prefix}log_karma',
1169
			array()
1170
		);
1171
	}
1172
1173
	// Emptying the error log?
1174
	if (!empty($_POST['empty_error']))
1175
		$smcFunc['db_query']('truncate_table', '
1176
			TRUNCATE {db_prefix}log_errors',
1177
			array(
1178
			)
1179
		);
1180
1181
	$changes = array();
1182
1183
	// Add proxy settings.
1184
	if (!isset($GLOBALS['image_proxy_maxsize']))
1185
		$changes += array(
1186
			'image_proxy_secret' => '\'' . substr(sha1(mt_rand()), 0, 20) . '\'',
1187
			'image_proxy_maxsize' => 5190,
1188
			'image_proxy_enabled' => 0,
1189
		);
1190
1191
	// If $boardurl reflects https, set force_ssl
1192
	if (!function_exists('cache_put_data'))
1193
		require_once($sourcedir . '/Load.php');
1194
	if (stripos($boardurl, 'https://') !== false)
1195
		updateSettings(array('force_ssl' => '1'));
1196
1197
	// If we're overriding the language follow it through.
1198
	if (isset($_GET['lang']) && file_exists($modSettings['theme_dir'] . '/languages/index.' . $_GET['lang'] . '.php'))
1199
		$changes['language'] = '\'' . $_GET['lang'] . '\'';
1200
1201
	if (!empty($_POST['maint']))
1202
	{
1203
		$changes['maintenance'] = '2';
1204
		// Remember what it was...
1205
		$upcontext['user']['main'] = $maintenance;
1206
1207
		if (!empty($_POST['maintitle']))
1208
		{
1209
			$changes['mtitle'] = '\'' . addslashes($_POST['maintitle']) . '\'';
1210
			$changes['mmessage'] = '\'' . addslashes($_POST['mainmessage']) . '\'';
1211
		}
1212
		else
1213
		{
1214
			$changes['mtitle'] = '\'Upgrading the forum...\'';
1215
			$changes['mmessage'] = '\'Don\\\'t worry, we will be back shortly with an updated forum.  It will only be a minute ;).\'';
1216
		}
1217
	}
1218
1219
	if ($command_line)
1220
		echo ' * Updating Settings.php...';
1221
1222
	// Fix some old paths.
1223
	if (substr($boarddir, 0, 1) == '.')
1224
		$changes['boarddir'] = '\'' . fixRelativePath($boarddir) . '\'';
1225
1226
	if (substr($sourcedir, 0, 1) == '.')
1227
		$changes['sourcedir'] = '\'' . fixRelativePath($sourcedir) . '\'';
1228
1229
	if (empty($cachedir) || substr($cachedir, 0, 1) == '.')
1230
		$changes['cachedir'] = '\'' . fixRelativePath($boarddir) . '/cache\'';
1231
1232
	// If they have a "host:port" setup for the host, split that into separate values
1233
	// You should never have a : in the hostname if you're not on MySQL, but better safe than sorry
1234
	if (strpos($db_server, ':') !== false && $db_type == 'mysql')
1235
	{
1236
		list ($db_server, $db_port) = explode(':', $db_server);
1237
1238
		$changes['db_server'] = '\'' . $db_server . '\'';
1239
1240
		// Only set this if we're not using the default port
1241
		if ($db_port != ini_get('mysqli.default_port'))
1242
			$changes['db_port'] = (int) $db_port;
1243
	}
1244
	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...
1245
	{
1246
		// If db_port is set and is the same as the default, set it to ''
1247
		if ($db_type == 'mysql')
1248
		{
1249
			if ($db_port == ini_get('mysqli.default_port'))
1250
				$changes['db_port'] = '\'\'';
1251
			elseif ($db_type == 'postgresql' && $db_port == 5432)
1252
				$changes['db_port'] = '\'\'';
1253
		}
1254
	}
1255
1256
	// Maybe we haven't had this option yet?
1257
	if (empty($packagesdir))
1258
		$changes['packagesdir'] = '\'' . fixRelativePath($boarddir) . '/Packages\'';
1259
1260
	// Add support for $tasksdir var.
1261
	if (empty($tasksdir))
1262
		$changes['tasksdir'] = '\'' . fixRelativePath($sourcedir) . '/tasks\'';
1263
1264
	// Make sure we fix the language as well.
1265
	if (stristr($language, '-utf8'))
1266
		$changes['language'] = '\'' . str_ireplace('-utf8', '', $language) . '\'';
1267
1268
	// @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...
1269
1270
	// Update Settings.php with the new settings.
1271
	require_once($sourcedir . '/Subs-Admin.php');
1272
	updateSettingsFile($changes);
1273
1274
	// Tell Settings.php to store db_last_error.php in the cache
1275
	move_db_last_error_to_cachedir();
1276
1277
	if ($command_line)
1278
		echo ' Successful.' . "\n";
1279
1280
	// Are we doing debug?
1281
	if (isset($_POST['debug']))
1282
	{
1283
		$upcontext['upgrade_status']['debug'] = true;
1284
		$is_debug = true;
1285
	}
1286
1287
	// If we're not backing up then jump one.
1288
	if (empty($_POST['backup']))
1289
		$upcontext['current_step']++;
1290
1291
	// If we've got here then let's proceed to the next step!
1292
	return true;
1293
}
1294
1295
// Backup the database - why not...
1296
function BackupDatabase()
1297
{
1298
	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...
1299
1300
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1301
	$upcontext['page_title'] = $txt['backup_database'];
1302
1303
	// Done it already - js wise?
1304
	if (!empty($_POST['backup_done']))
1305
		return true;
1306
1307
	// Some useful stuff here.
1308
	db_extend();
1309
1310
	// Might need this as well
1311
	db_extend('packages');
1312
1313
	// Get all the table names.
1314
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1315
	$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...
1316
	$tables = $smcFunc['db_list_tables']($db, $filter);
1317
1318
	$table_names = array();
1319
	foreach ($tables as $table)
1320
		if (substr($table, 0, 7) !== 'backup_')
1321
			$table_names[] = $table;
1322
1323
	$upcontext['table_count'] = count($table_names);
1324
	$upcontext['cur_table_num'] = $_GET['substep'];
1325
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1326
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1327
	// For non-java auto submit...
1328
	$file_steps = $upcontext['table_count'];
1329
1330
	// What ones have we already done?
1331 View Code Duplication
	foreach ($table_names as $id => $table)
1332
		if ($id < $_GET['substep'])
1333
			$upcontext['previous_tables'][] = $table;
1334
1335
	if ($command_line)
1336
		echo 'Backing Up Tables.';
1337
1338
	// If we don't support javascript we backup here.
1339
	if (!$support_js || isset($_GET['xml']))
1340
	{
1341
		// Backup each table!
1342
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1343
		{
1344
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1345
			$upcontext['cur_table_num'] = $substep + 1;
1346
1347
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1348
1349
			// Do we need to pause?
1350
			nextSubstep($substep);
1351
1352
			backupTable($table_names[$substep]);
1353
1354
			// If this is XML to keep it nice for the user do one table at a time anyway!
1355
			if (isset($_GET['xml']))
1356
				return upgradeExit();
1357
		}
1358
1359
		if ($command_line)
1360
		{
1361
			echo "\n" . ' Successful.\'' . "\n";
1362
			flush();
1363
		}
1364
		$upcontext['step_progress'] = 100;
1365
1366
		$_GET['substep'] = 0;
1367
		// Make sure we move on!
1368
		return true;
1369
	}
1370
1371
	// Either way next place to post will be database changes!
1372
	$_GET['substep'] = 0;
1373
	return false;
1374
}
1375
1376
// Backup one table...
1377
function backupTable($table)
1378
{
1379
	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...
1380
1381
	if ($command_line)
1382
	{
1383
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1384
		flush();
1385
	}
1386
1387
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1388
1389
	if ($command_line)
1390
		echo ' done.';
1391
}
1392
1393
// Step 2: Everything.
1394
function DatabaseChanges()
1395
{
1396
	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...
1397
	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...
1398
1399
	// Have we just completed this?
1400
	if (!empty($_POST['database_done']))
1401
		return true;
1402
1403
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1404
	$upcontext['page_title'] = $txt['database_changes'];
1405
1406
	// All possible files.
1407
	// Name, < version, insert_on_complete
1408
	$files = array(
1409
		array('upgrade_1-0.sql', '1.1', '1.1 RC0'),
1410
		array('upgrade_1-1.sql', '2.0', '2.0 a'),
1411
		array('upgrade_2-0_' . $db_type . '.sql', '2.1', '2.1 dev0'),
1412
		array('upgrade_2-1_' . $db_type . '.sql', '3.0', SMF_VERSION),
1413
	);
1414
1415
	// How many files are there in total?
1416
	if (isset($_GET['filecount']))
1417
		$upcontext['file_count'] = (int) $_GET['filecount'];
1418
	else
1419
	{
1420
		$upcontext['file_count'] = 0;
1421
		foreach ($files as $file)
1422
		{
1423
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1424
				$upcontext['file_count']++;
1425
		}
1426
	}
1427
1428
	// Do each file!
1429
	$did_not_do = count($files) - $upcontext['file_count'];
1430
	$upcontext['step_progress'] = 0;
1431
	$upcontext['cur_file_num'] = 0;
1432
	foreach ($files as $file)
1433
	{
1434
		if ($did_not_do)
1435
			$did_not_do--;
1436
		else
1437
		{
1438
			$upcontext['cur_file_num']++;
1439
			$upcontext['cur_file_name'] = $file[0];
1440
			// Do we actually need to do this still?
1441
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1442
			{
1443
				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1444
				if ($nextFile)
1445
				{
1446
					// Only update the version of this if complete.
1447
					$smcFunc['db_insert']('replace',
1448
						$db_prefix . 'settings',
1449
						array('variable' => 'string', 'value' => 'string'),
1450
						array('smfVersion', $file[2]),
1451
						array('variable')
1452
					);
1453
1454
					$modSettings['smfVersion'] = $file[2];
1455
				}
1456
1457
				// If this is XML we only do this stuff once.
1458
				if (isset($_GET['xml']))
1459
				{
1460
					// Flag to move on to the next.
1461
					$upcontext['completed_step'] = true;
1462
					// Did we complete the whole file?
1463
					if ($nextFile)
1464
						$upcontext['current_debug_item_num'] = -1;
1465
					return upgradeExit();
1466
				}
1467
				elseif ($support_js)
1468
					break;
1469
			}
1470
			// Set the progress bar to be right as if we had - even if we hadn't...
1471
			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1472
		}
1473
	}
1474
1475
	$_GET['substep'] = 0;
1476
	// So the template knows we're done.
1477
	if (!$support_js)
1478
	{
1479
		$upcontext['changes_complete'] = true;
1480
1481
		return true;
1482
	}
1483
	return false;
1484
}
1485
1486
1487
// Delete the damn thing!
1488
function DeleteUpgrade()
1489
{
1490
	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...
1491
	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...
1492
1493
	// Now it's nice to have some of the basic SMF source files.
1494
	if (!isset($_GET['ssi']) && !$command_line)
1495
		redirectLocation('&ssi=1');
1496
1497
	$upcontext['sub_template'] = 'upgrade_complete';
1498
	$upcontext['page_title'] = $txt['upgrade_complete'];
1499
1500
	$endl = $command_line ? "\n" : '<br>' . "\n";
1501
1502
	$changes = array(
1503
		'language' => '\'' . (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language) . '\'',
1504
		'db_error_send' => '1',
1505
		'upgradeData' => '\'\'',
1506
	);
1507
1508
	// Are we in maintenance mode?
1509
	if (isset($upcontext['user']['main']))
1510
	{
1511
		if ($command_line)
1512
			echo ' * ';
1513
		$upcontext['removed_maintenance'] = true;
1514
		$changes['maintenance'] = $upcontext['user']['main'];
1515
	}
1516
	// Otherwise if somehow we are in 2 let's go to 1.
1517
	elseif (!empty($maintenance) && $maintenance == 2)
1518
		$changes['maintenance'] = 1;
1519
1520
	// Wipe this out...
1521
	$upcontext['user'] = array();
1522
1523
	require_once($sourcedir . '/Subs-Admin.php');
1524
	updateSettingsFile($changes);
1525
1526
	// Clean any old cache files away.
1527
	upgrade_clean_cache();
1528
1529
	// Can we delete the file?
1530
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1531
1532
	// Now is the perfect time to fetch the SM files.
1533
	if ($command_line)
1534
		cli_scheduled_fetchSMfiles();
1535
	else
1536
	{
1537
		require_once($sourcedir . '/ScheduledTasks.php');
1538
		$forum_version = SMF_VERSION; // The variable is usually defined in index.php so lets just use the constant to do it for us.
1539
		scheduled_fetchSMfiles(); // Now go get those files!
1540
		// This is needed in case someone invokes the upgrader using https when upgrading an http forum
1541 View Code Duplication
		if (httpsOn())
1542
			$settings['default_theme_url'] = strtr($settings['default_theme_url'], array('http://' => 'https://'));
1543
	}
1544
1545
	// Log what we've done.
1546
	if (empty($user_info['id']))
1547
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
1548
1549
	// Log the action manually, so CLI still works.
1550
	$smcFunc['db_insert']('',
1551
		'{db_prefix}log_actions',
1552
		array(
1553
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'inet', 'action' => 'string',
1554
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
1555
		),
1556
		array(
1557
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
1558
			0, 0, 0, json_encode(array('version' => $forum_version, 'member' => $user_info['id'])),
1559
		),
1560
		array('id_action')
1561
	);
1562
	$user_info['id'] = 0;
1563
1564
	// Save the current database version.
1565
	$server_version = $smcFunc['db_server_info']();
1566 View Code Duplication
	if ($db_type == 'mysql' && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51')))
1567
		updateSettings(array('db_mysql_group_by_fix' => '1'));
1568
1569
	if ($command_line)
1570
	{
1571
		echo $endl;
1572
		echo 'Upgrade Complete!', $endl;
1573
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
1574
		exit;
1575
	}
1576
1577
	// Make sure it says we're done.
1578
	$upcontext['overall_percent'] = 100;
1579
	if (isset($upcontext['step_progress']))
1580
		unset($upcontext['step_progress']);
1581
1582
	$_GET['substep'] = 0;
1583
	return false;
1584
}
1585
1586
// Just like the built in one, but setup for CLI to not use themes.
1587
function cli_scheduled_fetchSMfiles()
1588
{
1589
	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...
1590
1591
	if (empty($modSettings['time_format']))
1592
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1593
1594
	// What files do we want to get
1595
	$request = $smcFunc['db_query']('', '
1596
		SELECT id_file, filename, path, parameters
1597
		FROM {db_prefix}admin_info_files',
1598
		array(
1599
		)
1600
	);
1601
1602
	$js_files = array();
1603 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
1604
	{
1605
		$js_files[$row['id_file']] = array(
1606
			'filename' => $row['filename'],
1607
			'path' => $row['path'],
1608
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode($forum_version)),
1609
		);
1610
	}
1611
	$smcFunc['db_free_result']($request);
1612
1613
	// We're gonna need fetch_web_data() to pull this off.
1614
	require_once($sourcedir . '/Subs.php');
1615
1616
	foreach ($js_files as $ID_FILE => $file)
1617
	{
1618
		// Create the url
1619
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'https://www.simplemachines.org' : '';
1620
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1621
1622
		// Get the file
1623
		$file_data = fetch_web_data($url);
1624
1625
		// If we got an error - give up - the site might be down.
1626
		if ($file_data === false)
1627
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
1628
1629
		// Save the file to the database.
1630
		$smcFunc['db_query']('substring', '
1631
			UPDATE {db_prefix}admin_info_files
1632
			SET data = SUBSTRING({string:file_data}, 1, 65534)
1633
			WHERE id_file = {int:id_file}',
1634
			array(
1635
				'id_file' => $ID_FILE,
1636
				'file_data' => $file_data,
1637
			)
1638
		);
1639
	}
1640
	return true;
1641
}
1642
1643
function convertSettingsToTheme()
1644
{
1645
	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...
1646
1647
	$values = array(
1648
		'show_latest_member' => @$GLOBALS['showlatestmember'],
1649
		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
1650
		'show_modify' => @$GLOBALS['showmodify'],
1651
		'show_user_images' => @$GLOBALS['showuserpic'],
1652
		'show_blurb' => @$GLOBALS['showusertext'],
1653
		'show_gender' => @$GLOBALS['showgenderimage'],
1654
		'show_newsfader' => @$GLOBALS['shownewsfader'],
1655
		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
1656
		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
1657
		'linktree_link' => @$GLOBALS['curposlinks'],
1658
		'show_profile_buttons' => @$GLOBALS['profilebutton'],
1659
		'show_mark_read' => @$GLOBALS['showmarkread'],
1660
		'newsfader_time' => @$GLOBALS['fadertime'],
1661
		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
1662
		'enable_news' => @$GLOBALS['enable_news'],
1663
		'return_to_post' => @$modSettings['returnToPost'],
1664
	);
1665
1666
	$themeData = array();
1667
	foreach ($values as $variable => $value)
1668
	{
1669
		if (!isset($value) || $value === null)
1670
			$value = 0;
1671
1672
		$themeData[] = array(0, 1, $variable, $value);
1673
	}
1674 View Code Duplication
	if (!empty($themeData))
1675
	{
1676
		$smcFunc['db_insert']('ignore',
1677
			$db_prefix . 'themes',
1678
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
1679
			$themeData,
1680
			array('id_member', 'id_theme', 'variable')
1681
		);
1682
	}
1683
}
1684
1685
// This function only works with MySQL but that's fine as it is only used for v1.0.
1686
function convertSettingstoOptions()
1687
{
1688
	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...
1689
1690
	// Format: new_setting -> old_setting_name.
1691
	$values = array(
1692
		'calendar_start_day' => 'cal_startmonday',
1693
		'view_newest_first' => 'viewNewestFirst',
1694
		'view_newest_pm_first' => 'viewNewestFirst',
1695
	);
1696
1697
	foreach ($values as $variable => $value)
1698
	{
1699
		if (empty($modSettings[$value[0]]))
1700
			continue;
1701
1702
		$smcFunc['db_query']('', '
1703
			INSERT IGNORE INTO {db_prefix}themes
1704
				(id_member, id_theme, variable, value)
1705
			SELECT id_member, 1, {string:variable}, {string:value}
1706
			FROM {db_prefix}members',
1707
			array(
1708
				'variable' => $variable,
1709
				'value' => $modSettings[$value[0]],
1710
				'db_error_skip' => true,
1711
			)
1712
		);
1713
1714
		$smcFunc['db_query']('', '
1715
			INSERT IGNORE INTO {db_prefix}themes
1716
				(id_member, id_theme, variable, value)
1717
			VALUES (-1, 1, {string:variable}, {string:value})',
1718
			array(
1719
				'variable' => $variable,
1720
				'value' => $modSettings[$value[0]],
1721
				'db_error_skip' => true,
1722
			)
1723
		);
1724
	}
1725
}
1726
1727
function php_version_check()
1728
{
1729
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
1730
}
1731
1732
function db_version_check()
1733
{
1734
	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...
1735
1736
	$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...
1737
	$curver = preg_replace('~\-.+?$~', '', $curver);
1738
1739
	return version_compare($databases[$db_type]['version'], $curver, '<=');
1740
}
1741
1742
function fixRelativePath($path)
1743
{
1744
	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...
1745
1746
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
1747
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
1748
}
1749
1750
function parse_sql($filename)
1751
{
1752
	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...
1753
	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...
1754
1755
/*
1756
	Failure allowed on:
1757
		- INSERT INTO but not INSERT IGNORE INTO.
1758
		- UPDATE IGNORE but not UPDATE.
1759
		- ALTER TABLE and ALTER IGNORE TABLE.
1760
		- DROP TABLE.
1761
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
1762
1763
	If a comment...
1764
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
1765
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
1766
		- is only ---#, it is "done." and then a break - only shown in debug.
1767
		- begins with ---{ it is a code block terminating at ---}.
1768
1769
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
1770
1771
	Replaces the following variables:
1772
		- {$boarddir}
1773
		- {$boardurl}
1774
		- {$db_prefix}
1775
		- {$db_collation}
1776
*/
1777
1778
	// May want to use extended functionality.
1779
	db_extend();
1780
	db_extend('packages');
1781
1782
	// Our custom error handler - does nothing but does stop public errors from XML!
1783
	set_error_handler(
1784
		function ($errno, $errstr, $errfile, $errline) use ($support_js)
1785
		{
1786
			if ($support_js)
1787
				return true;
1788
			else
1789
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
1790
		}
1791
	);
1792
1793
	// If we're on MySQL, set {db_collation}; this approach is used throughout upgrade_2-0_mysql.php to set new tables to utf8
1794
	// Note it is expected to be in the format: ENGINE=MyISAM{$db_collation};
1795
	if ($db_type == 'mysql')
1796
		$db_collation = ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1797
	else
1798
		$db_collation = '';
1799
1800
	$endl = $command_line ? "\n" : '<br>' . "\n";
1801
1802
	$lines = file($filename);
1803
1804
	$current_type = 'sql';
1805
	$current_data = '';
1806
	$substep = 0;
1807
	$last_step = '';
1808
1809
	// Make sure all newly created tables will have the proper characters set; this approach is used throughout upgrade_2-1_mysql.php
1810
	if (isset($db_character_set) && $db_character_set === 'utf8')
1811
		$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
1812
1813
	// Count the total number of steps within this file - for progress.
1814
	$file_steps = substr_count(implode('', $lines), '---#');
1815
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
1816
	$upcontext['debug_items'] = $file_steps;
1817
	$upcontext['current_item_num'] = 0;
1818
	$upcontext['current_item_name'] = '';
1819
	$upcontext['current_debug_item_num'] = 0;
1820
	$upcontext['current_debug_item_name'] = '';
1821
	// This array keeps a record of what we've done in case java is dead...
1822
	$upcontext['actioned_items'] = array();
1823
1824
	$done_something = false;
1825
1826
	foreach ($lines as $line_number => $line)
1827
	{
1828
		$do_current = $substep >= $_GET['substep'];
1829
1830
		// Get rid of any comments in the beginning of the line...
1831
		if (substr(trim($line), 0, 2) === '/*')
1832
			$line = preg_replace('~/\*.+?\*/~', '', $line);
1833
1834
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
1835
		if ($is_debug && !$support_js && $command_line)
1836
			flush();
1837
1838
		if (trim($line) === '')
1839
			continue;
1840
1841
		if (trim(substr($line, 0, 3)) === '---')
1842
		{
1843
			$type = substr($line, 3, 1);
1844
1845
			// An error??
1846
			if (trim($current_data) != '' && $type !== '}')
1847
			{
1848
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
1849
				if ($command_line)
1850
					echo $upcontext['error_message'];
1851
			}
1852
1853
			if ($type == ' ')
1854
			{
1855
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
1856
				{
1857
					echo ' Successful.', $endl;
1858
					flush();
1859
				}
1860
1861
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
1862
				$upcontext['current_item_num']++;
1863
				$upcontext['current_item_name'] = $last_step;
1864
1865
				if ($do_current)
1866
				{
1867
					$upcontext['actioned_items'][] = $last_step;
1868
					if ($command_line)
1869
						echo ' * ';
1870
				}
1871
			}
1872
			elseif ($type == '#')
1873
			{
1874
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
1875
1876
				$upcontext['current_debug_item_num']++;
1877
				if (trim($line) != '---#')
1878
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
1879
1880
				// Have we already done something?
1881
				if (isset($_GET['xml']) && $done_something)
1882
				{
1883
					restore_error_handler();
1884
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
1885
				}
1886
1887
				if ($do_current)
1888
				{
1889
					if (trim($line) == '---#' && $command_line)
1890
						echo ' done.', $endl;
1891
					elseif ($command_line)
1892
						echo ' +++ ', rtrim(substr($line, 4));
1893
					elseif (trim($line) != '---#')
1894
					{
1895
						if ($is_debug)
1896
							$upcontext['actioned_items'][] = htmlspecialchars(rtrim(substr($line, 4)));
1897
					}
1898
				}
1899
1900
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
1901
				{
1902
					if ($command_line)
1903
						echo ' * ';
1904
					else
1905
						$upcontext['actioned_items'][] = $last_step;
1906
				}
1907
1908
				// Small step - only if we're actually doing stuff.
1909
				if ($do_current)
1910
					nextSubstep(++$substep);
1911
				else
1912
					$substep++;
1913
			}
1914
			elseif ($type == '{')
1915
				$current_type = 'code';
1916
			elseif ($type == '}')
1917
			{
1918
				$current_type = 'sql';
1919
1920
				if (!$do_current)
1921
				{
1922
					$current_data = '';
1923
					continue;
1924
				}
1925
1926
				if (eval('global $db_prefix, $modSettings, $smcFunc, $txt; ' . $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...
1927
				{
1928
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
1929
					if ($command_line)
1930
						echo $upcontext['error_message'];
1931
				}
1932
1933
				// Done with code!
1934
				$current_data = '';
1935
				$done_something = true;
1936
			}
1937
1938
			continue;
1939
		}
1940
1941
		$current_data .= $line;
1942
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
1943
		{
1944
			if ((!$support_js || isset($_GET['xml'])))
1945
			{
1946
				if (!$do_current)
1947
				{
1948
					$current_data = '';
1949
					continue;
1950
				}
1951
1952
				$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));
1953
1954
				upgrade_query($current_data);
1955
1956
				// @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...
1957
				/*
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...
1958
				$result = $smcFunc['db_query']('', $current_data, false, false);
1959
				// Went wrong?
1960
				if (!$result)
1961
				{
1962
					// Bit of a bodge - do we want the error?
1963
					if (!empty($upcontext['return_error']))
1964
					{
1965
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
1966
						return false;
1967
					}
1968
				}*/
1969
				$done_something = true;
1970
			}
1971
			$current_data = '';
1972
		}
1973
		// If this is xml based and we're just getting the item name then that's grand.
1974
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
1975
		{
1976
			restore_error_handler();
1977
			return false;
1978
		}
1979
1980
		// Clean up by cleaning any step info.
1981
		$step_progress = array();
1982
		$custom_warning = '';
1983
	}
1984
1985
	// Put back the error handler.
1986
	restore_error_handler();
1987
1988
	if ($command_line)
1989
	{
1990
		echo ' Successful.' . "\n";
1991
		flush();
1992
	}
1993
1994
	$_GET['substep'] = 0;
1995
	return true;
1996
}
1997
1998
function upgrade_query($string, $unbuffered = false)
1999
{
2000
	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...
2001
	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...
2002
2003
	// Get the query result - working around some SMF specific security - just this once!
2004
	$modSettings['disableQueryCheck'] = true;
2005
	$db_unbuffered = $unbuffered;
2006
	$ignore_insert_error = false;
2007
2008
	// If we got an old pg version and use a insert ignore query
2009
	if ($db_type == 'postgresql' && !$smcFunc['db_native_replace']() && strpos($string, 'ON CONFLICT DO NOTHING') !== false)
2010
	{
2011
		$ignore_insert_error = true;
2012
		$string = str_replace('ON CONFLICT DO NOTHING', '', $string);
2013
	}
2014
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
2015
	$db_unbuffered = false;
2016
2017
	// Failure?!
2018
	if ($result !== false)
2019
		return $result;
2020
2021
	$db_error_message = $smcFunc['db_error']($db_connection);
2022
	// If MySQL we do something more clever.
2023
	if ($db_type == 'mysql')
2024
	{
2025
		$mysqli_errno = mysqli_errno($db_connection);
2026
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR'));
2027
2028
		// Error numbers:
2029
		//    1016: Can't open file '....MYI'
2030
		//    1050: Table already exists.
2031
		//    1054: Unknown column name.
2032
		//    1060: Duplicate column name.
2033
		//    1061: Duplicate key name.
2034
		//    1062: Duplicate entry for unique key.
2035
		//    1068: Multiple primary keys.
2036
		//    1072: Key column '%s' doesn't exist in table.
2037
		//    1091: Can't drop key, doesn't exist.
2038
		//    1146: Table doesn't exist.
2039
		//    2013: Lost connection to server during query.
2040
2041
		if ($mysqli_errno == 1016)
2042
		{
2043
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
2044
			{
2045
				mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
2046
				$result = mysqli_query($db_connection, $string);
2047
				if ($result !== false)
2048
					return $result;
2049
			}
2050
		}
2051
		elseif ($mysqli_errno == 2013)
2052
		{
2053
			$db_connection = mysqli_connect($db_server, $db_user, $db_passwd);
2054
			mysqli_select_db($db_connection, $db_name);
2055
			if ($db_connection)
2056
			{
2057
				$result = mysqli_query($db_connection, $string);
2058
				if ($result !== false)
2059
					return $result;
2060
			}
2061
		}
2062
		// Duplicate column name... should be okay ;).
2063 View Code Duplication
		elseif (in_array($mysqli_errno, array(1060, 1061, 1068, 1091)))
2064
			return false;
2065
		// Duplicate insert... make sure it's the proper type of query ;).
2066 View Code Duplication
		elseif (in_array($mysqli_errno, array(1054, 1062, 1146)) && $error_query)
2067
			return false;
2068
		// Creating an index on a non-existent column.
2069
		elseif ($mysqli_errno == 1072)
2070
			return false;
2071
		elseif ($mysqli_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
2072
			return false;
2073
	}
2074
	// If a table already exists don't go potty.
2075
	else
2076
	{
2077
		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U')))
2078
		{
2079
			if (strpos($db_error_message, 'exist') !== false)
2080
				return true;
2081
		}
2082
		elseif (strpos(trim($string), 'INSERT ') !== false)
2083
		{
2084
			if (strpos($db_error_message, 'duplicate') !== false || $ignore_insert_error)
2085
				return true;
2086
		}
2087
	}
2088
2089
	// Get the query string so we pass everything.
2090
	$query_string = '';
2091
	foreach ($_GET as $k => $v)
2092
		$query_string .= ';' . $k . '=' . $v;
2093
	if (strlen($query_string) != 0)
2094
		$query_string = '?' . substr($query_string, 1);
2095
2096
	if ($command_line)
2097
	{
2098
		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
2099
		die;
2100
	}
2101
2102
	// Bit of a bodge - do we want the error?
2103
	if (!empty($upcontext['return_error']))
2104
	{
2105
		$upcontext['error_message'] = $db_error_message;
2106
		$upcontext['error_string'] = $string;
2107
		return false;
2108
	}
2109
2110
	// Otherwise we have to display this somewhere appropriate if possible.
2111
	$upcontext['forced_error_message'] = '
2112
			<strong>'. $txt['upgrade_unsuccessful'] .'</strong><br>
0 ignored issues
show
Bug introduced by
The variable $txt does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
2113
2114
			<div style="margin: 2ex;">
2115
				'. $txt['upgrade_thisquery'] .'
2116
				<blockquote><pre>' . nl2br(htmlspecialchars(trim($string))) . ';</pre></blockquote>
2117
2118
				'. $txt['upgrade_causerror'] .'
2119
				<blockquote>' . nl2br(htmlspecialchars($db_error_message)) . '</blockquote>
2120
			</div>
2121
2122
			<form action="' . $upgradeurl . $query_string . '" method="post">
2123
				<input type="submit" value="Try again" class="button">
2124
			</form>
2125
		</div>';
2126
2127
	upgradeExit();
2128
}
2129
2130
// This performs a table alter, but does it unbuffered so the script can time out professionally.
2131
function protected_alter($change, $substep, $is_test = false)
2132
{
2133
	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...
2134
2135
	db_extend('packages');
2136
2137
	// Firstly, check whether the current index/column exists.
2138
	$found = false;
2139
	if ($change['type'] === 'column')
2140
	{
2141
		$columns = $smcFunc['db_list_columns']('{db_prefix}' . $change['table'], true);
2142
		foreach ($columns as $column)
2143
		{
2144
			// Found it?
2145
			if ($column['name'] === $change['name'])
2146
			{
2147
				$found |= 1;
2148
				// Do some checks on the data if we have it set.
2149
				if (isset($change['col_type']))
2150
					$found &= $change['col_type'] === $column['type'];
2151
				if (isset($change['null_allowed']))
2152
					$found &= $column['null'] == $change['null_allowed'];
2153
				if (isset($change['default']))
2154
					$found &= $change['default'] === $column['default'];
2155
			}
2156
		}
2157
	}
2158
	elseif ($change['type'] === 'index')
2159
	{
2160
		$request = upgrade_query('
2161
			SHOW INDEX
2162
			FROM ' . $db_prefix . $change['table']);
2163
		if ($request !== false)
2164
		{
2165
			$cur_index = array();
2166
2167
			while ($row = $smcFunc['db_fetch_assoc']($request))
2168
				if ($row['Key_name'] === $change['name'])
2169
					$cur_index[(int) $row['Seq_in_index']] = $row['Column_name'];
2170
2171
			ksort($cur_index, SORT_NUMERIC);
2172
			$found = array_values($cur_index) === $change['target_columns'];
2173
2174
			$smcFunc['db_free_result']($request);
2175
		}
2176
	}
2177
2178
	// If we're trying to add and it's added, we're done.
2179
	if ($found && in_array($change['method'], array('add', 'change')))
2180
		return true;
2181
	// Otherwise if we're removing and it wasn't found we're also done.
2182
	elseif (!$found && in_array($change['method'], array('remove', 'change_remove')))
2183
		return true;
2184
	// Otherwise is it just a test?
2185
	elseif ($is_test)
2186
		return false;
2187
2188
	// Not found it yet? Bummer! How about we see if we're currently doing it?
2189
	$running = false;
2190
	$found = false;
2191
	while (1 == 1)
2192
	{
2193
		$request = upgrade_query('
2194
			SHOW FULL PROCESSLIST');
2195
		while ($row = $smcFunc['db_fetch_assoc']($request))
2196
		{
2197
			if (strpos($row['Info'], 'ALTER TABLE ' . $db_prefix . $change['table']) !== false && strpos($row['Info'], $change['text']) !== false)
2198
				$found = true;
2199
		}
2200
2201
		// Can't find it? Then we need to run it fools!
2202
		if (!$found && !$running)
2203
		{
2204
			$smcFunc['db_free_result']($request);
2205
2206
			$success = upgrade_query('
2207
				ALTER TABLE ' . $db_prefix . $change['table'] . '
2208
				' . $change['text'], true) !== false;
2209
2210
			if (!$success)
2211
				return false;
2212
2213
			// Return
2214
			$running = true;
2215
		}
2216
		// What if we've not found it, but we'd ran it already? Must of completed.
2217
		elseif (!$found)
2218
		{
2219
			$smcFunc['db_free_result']($request);
2220
			return true;
2221
		}
2222
2223
		// Pause execution for a sec or three.
2224
		sleep(3);
2225
2226
		// Can never be too well protected.
2227
		nextSubstep($substep);
2228
	}
2229
2230
	// Protect it.
2231
	nextSubstep($substep);
2232
}
2233
2234
/**
2235
 * Alter a text column definition preserving its character set.
2236
 *
2237
 * @param array $change
2238
 * @param int $substep
2239
 */
2240
function textfield_alter($change, $substep)
2241
{
2242
	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...
2243
2244
	$request = $smcFunc['db_query']('', '
2245
		SHOW FULL COLUMNS
2246
		FROM {db_prefix}' . $change['table'] . '
2247
		LIKE {string:column}',
2248
		array(
2249
			'column' => $change['column'],
2250
			'db_error_skip' => true,
2251
		)
2252
	);
2253
	if ($smcFunc['db_num_rows']($request) === 0)
2254
		die('Unable to find column ' . $change['column'] . ' inside table ' . $db_prefix . $change['table']);
2255
	$table_row = $smcFunc['db_fetch_assoc']($request);
2256
	$smcFunc['db_free_result']($request);
2257
2258
	// If something of the current column definition is different, fix it.
2259
	$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']);
2260
2261
	// Columns that previously allowed null, need to be converted first.
2262
	$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
2263
2264
	// Get the character set that goes with the collation of the column.
2265 View Code Duplication
	if ($column_fix && !empty($table_row['Collation']))
2266
	{
2267
		$request = $smcFunc['db_query']('', '
2268
			SHOW COLLATION
2269
			LIKE {string:collation}',
2270
			array(
2271
				'collation' => $table_row['Collation'],
2272
				'db_error_skip' => true,
2273
			)
2274
		);
2275
		// No results? Just forget it all together.
2276
		if ($smcFunc['db_num_rows']($request) === 0)
2277
			unset($table_row['Collation']);
2278
		else
2279
			$collation_info = $smcFunc['db_fetch_assoc']($request);
2280
		$smcFunc['db_free_result']($request);
2281
	}
2282
2283
	if ($column_fix)
2284
	{
2285
		// Make sure there are no NULL's left.
2286
		if ($null_fix)
2287
			$smcFunc['db_query']('', '
2288
				UPDATE {db_prefix}' . $change['table'] . '
2289
				SET ' . $change['column'] . ' = {string:default}
2290
				WHERE ' . $change['column'] . ' IS NULL',
2291
				array(
2292
					'default' => isset($change['default']) ? $change['default'] : '',
2293
					'db_error_skip' => true,
2294
				)
2295
			);
2296
2297
		// Do the actual alteration.
2298
		$smcFunc['db_query']('', '
2299
			ALTER TABLE {db_prefix}' . $change['table'] . '
2300
			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}' : ''),
2301
			array(
2302
				'default' => isset($change['default']) ? $change['default'] : '',
2303
				'db_error_skip' => true,
2304
			)
2305
		);
2306
	}
2307
	nextSubstep($substep);
2308
}
2309
2310
// Check if we need to alter this query.
2311
function checkChange(&$change)
2312
{
2313
	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...
2314
	static $database_version, $where_field_support;
2315
2316
	// Attempt to find a database_version.
2317
	if (empty($database_version))
2318
	{
2319
		$database_version = $databases[$db_type]['version_check'];
2320
		$where_field_support = $db_type == 'mysql' && version_compare('5.0', $database_version, '<=');
2321
	}
2322
2323
	// Not a column we need to check on?
2324
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
2325
		return;
2326
2327
	// Break it up you (six|seven).
2328
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
2329
2330
	// Can we support a shortcut method?
2331
	if ($where_field_support)
2332
	{
2333
		// Get the details about this change.
2334
		$request = $smcFunc['db_query']('', '
2335
			SHOW FIELDS
2336
			FROM {db_prefix}{raw:table}
2337
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
2338
			array(
2339
				'table' => $change['table'],
2340
				'old_name' => $temp[1],
2341
				'new_name' => $temp[2],
2342
		));
2343
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2344
		if ($smcFunc['db_num_rows'] != 1)
2345
			return;
2346
2347
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
2348
		$smcFunc['db_free_result']($request);
2349
	}
2350
	else
2351
	{
2352
		// Do this the old fashion, sure method way.
2353
		$request = $smcFunc['db_query']('', '
2354
			SHOW FIELDS
2355
			FROM {db_prefix}{raw:table}',
2356
			array(
2357
				'table' => $change['table'],
2358
		));
2359
		// Mayday!
2360
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2361
		if ($smcFunc['db_num_rows'] == 0)
2362
			return;
2363
2364
		// Oh where, oh where has my little field gone. Oh where can it be...
2365
		while ($row = $smcFunc['db_query']($request))
2366
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
2367
			{
2368
				$current_type = $row['Type'];
2369
				break;
2370
			}
2371
	}
2372
2373
	// If this doesn't match, the column may of been altered for a reason.
2374
	if (trim($current_type) != trim($temp[3]))
2375
		$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...
2376
2377
	// Piece this back together.
2378
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
2379
}
2380
2381
// The next substep.
2382
function nextSubstep($substep)
2383
{
2384
	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...
2385
	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...
2386
2387
	if ($_GET['substep'] < $substep)
2388
		$_GET['substep'] = $substep;
2389
2390
	if ($command_line)
2391
	{
2392
		if (time() - $start_time > 1 && empty($is_debug))
2393
		{
2394
			echo '.';
2395
			$start_time = time();
2396
		}
2397
		return;
2398
	}
2399
2400
	@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...
2401
	if (function_exists('apache_reset_timeout'))
2402
		@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...
2403
2404
	if (time() - $start_time <= $timeLimitThreshold)
2405
		return;
2406
2407
	// Do we have some custom step progress stuff?
2408
	if (!empty($step_progress))
2409
	{
2410
		$upcontext['substep_progress'] = 0;
2411
		$upcontext['substep_progress_name'] = $step_progress['name'];
2412
		if ($step_progress['current'] > $step_progress['total'])
2413
			$upcontext['substep_progress'] = 99.9;
2414
		else
2415
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2416
2417
		// Make it nicely rounded.
2418
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2419
	}
2420
2421
	// If this is XML we just exit right away!
2422
	if (isset($_GET['xml']))
2423
		return upgradeExit();
2424
2425
	// We're going to pause after this!
2426
	$upcontext['pause'] = true;
2427
2428
	$upcontext['query_string'] = '';
2429
	foreach ($_GET as $k => $v)
2430
	{
2431
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2432
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2433
	}
2434
2435
	// Custom warning?
2436
	if (!empty($custom_warning))
2437
		$upcontext['custom_warning'] = $custom_warning;
2438
2439
	upgradeExit();
2440
}
2441
2442
function cmdStep0()
2443
{
2444
	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...
2445
	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...
2446
	$start_time = time();
2447
2448
	ob_end_clean();
2449
	ob_implicit_flush(true);
2450
	@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...
2451
2452
	if (!isset($_SERVER['argv']))
2453
		$_SERVER['argv'] = array();
2454
	$_GET['maint'] = 1;
2455
2456
	foreach ($_SERVER['argv'] as $i => $arg)
2457
	{
2458
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2459
			$_GET['lang'] = $match[1];
2460
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2461
			continue;
2462
		elseif ($arg == '--no-maintenance')
2463
			$_GET['maint'] = 0;
2464
		elseif ($arg == '--debug')
2465
			$is_debug = true;
2466
		elseif ($arg == '--backup')
2467
			$_POST['backup'] = 1;
2468
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2469
			$_GET['conv'] = 1;
2470
		elseif ($i != 0)
2471
		{
2472
			echo 'SMF Command-line Upgrader
2473
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2474
2475
    --language=LANG         Reset the forum\'s language to LANG.
2476
    --no-maintenance        Don\'t put the forum into maintenance mode.
2477
    --debug                 Output debugging information.
2478
    --backup                Create backups of tables with "backup_" prefix.';
2479
			echo "\n";
2480
			exit;
2481
		}
2482
	}
2483
2484
	if (!php_version_check())
2485
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
2486
	if (!db_version_check())
2487
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2488
2489
	// Do some checks to make sure they have proper privileges
2490
	db_extend('packages');
2491
2492
	// CREATE
2493
	$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');
2494
2495
	// ALTER
2496
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
2497
2498
	// DROP
2499
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2500
2501
	// Sorry... we need CREATE, ALTER and DROP
2502 View Code Duplication
	if (!$create || !$alter || !$drop)
2503
		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...
2504
2505
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2506
		&& @file_exists($sourcedir . '/QueryString.php')
2507
		&& @file_exists($sourcedir . '/ManageBoards.php');
2508
	if (!$check && !isset($modSettings['smfVersion']))
2509
		print_error('Error: Some files are missing or out-of-date.', true);
2510
2511
	// Do a quick version spot check.
2512
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
2513
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2514
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2515
		print_error('Error: Some files have not yet been updated properly.');
2516
2517
	// Make sure Settings.php is writable.
2518
	quickFileWritable($boarddir . '/Settings.php');
2519
	if (!is_writable($boarddir . '/Settings.php'))
2520
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2521
2522
	// Make sure Settings_bak.php is writable.
2523
	quickFileWritable($boarddir . '/Settings_bak.php');
2524
	if (!is_writable($boarddir . '/Settings_bak.php'))
2525
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2526
2527 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
2528
		print_error('Error: Unable to obtain write access to "agreement.txt".');
2529
	elseif (isset($modSettings['agreement']))
2530
	{
2531
		$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...
2532
		fwrite($fp, $modSettings['agreement']);
2533
		fclose($fp);
2534
	}
2535
2536
	// Make sure Themes is writable.
2537
	quickFileWritable($modSettings['theme_dir']);
2538
2539
	if (!is_writable($modSettings['theme_dir']) && !isset($modSettings['smfVersion']))
2540
		print_error('Error: Unable to obtain write access to "Themes".');
2541
2542
	// Make sure cache directory exists and is writable!
2543
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
2544
	if (!file_exists($cachedir_temp))
2545
		@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...
2546
2547
	// Make sure the cache temp dir is writable.
2548
	quickFileWritable($cachedir_temp);
2549
2550
	if (!is_writable($cachedir_temp))
2551
		print_error('Error: Unable to obtain write access to "cache".', true);
2552
2553
	// Make sure db_last_error.php is writable.
2554
	quickFileWritable($cachedir_temp . '/db_last_error.php');
2555
	if (!is_writable($cachedir_temp . '/db_last_error.php'))
2556
		print_error('Error: Unable to obtain write access to "db_last_error.php".');
2557
2558
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
2559
		print_error('Error: Unable to find language files!', true);
2560
	else
2561
	{
2562
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
2563
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
2564
2565
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
2566
			print_error('Error: Language files out of date.', true);
2567
		if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
2568
			print_error('Error: Install language is missing for selected language.', true);
2569
2570
		// Otherwise include it!
2571
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
2572
	}
2573
2574
	// Make sure we skip the HTML for login.
2575
	$_POST['upcont'] = true;
2576
	$upcontext['current_step'] = 1;
2577
}
2578
2579
/**
2580
 * Handles converting your database to UTF-8
2581
 */
2582
function ConvertUtf8()
2583
{
2584
	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...
2585
	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...
2586
2587
	// Done it already?
2588
	if (!empty($_POST['utf8_done']))
2589
		return true;
2590
2591
	// First make sure they aren't already on UTF-8 before we go anywhere...
2592
	if ($db_type == 'postgresql' || ($db_character_set === 'utf8' && !empty($modSettings['global_character_set']) && $modSettings['global_character_set'] === 'UTF-8'))
2593
	{
2594
		$smcFunc['db_insert']('replace',
2595
			'{db_prefix}settings',
2596
			array('variable' => 'string', 'value' => 'string'),
2597
			array(array('global_character_set', 'UTF-8')),
2598
			array('variable')
2599
		);
2600
2601
		return true;
2602
	}
2603
	else
2604
	{
2605
		$upcontext['page_title'] = $txt['converting_utf8'];
2606
		$upcontext['sub_template'] = isset($_GET['xml']) ? 'convert_xml' : 'convert_utf8';
2607
2608
		// The character sets used in SMF's language files with their db equivalent.
2609
		$charsets = array(
2610
			// Armenian
2611
			'armscii8' => 'armscii8',
2612
			// Chinese-traditional.
2613
			'big5' => 'big5',
2614
			// Chinese-simplified.
2615
			'gbk' => 'gbk',
2616
			// West European.
2617
			'ISO-8859-1' => 'latin1',
2618
			// Romanian.
2619
			'ISO-8859-2' => 'latin2',
2620
			// Turkish.
2621
			'ISO-8859-9' => 'latin5',
2622
			// Latvian
2623
			'ISO-8859-13' => 'latin7',
2624
			// West European with Euro sign.
2625
			'ISO-8859-15' => 'latin9',
2626
			// Thai.
2627
			'tis-620' => 'tis620',
2628
			// Persian, Chinese, etc.
2629
			'UTF-8' => 'utf8',
2630
			// Russian.
2631
			'windows-1251' => 'cp1251',
2632
			// Greek.
2633
			'windows-1253' => 'utf8',
2634
			// Hebrew.
2635
			'windows-1255' => 'utf8',
2636
			// Arabic.
2637
			'windows-1256' => 'cp1256',
2638
		);
2639
2640
		// Get a list of character sets supported by your MySQL server.
2641
		$request = $smcFunc['db_query']('', '
2642
			SHOW CHARACTER SET',
2643
			array(
2644
			)
2645
		);
2646
		$db_charsets = array();
2647
		while ($row = $smcFunc['db_fetch_assoc']($request))
2648
			$db_charsets[] = $row['Charset'];
2649
2650
		$smcFunc['db_free_result']($request);
2651
2652
		// Character sets supported by both MySQL and SMF's language files.
2653
		$charsets = array_intersect($charsets, $db_charsets);
2654
2655
		// Use the messages.body column as indicator for the database charset.
2656
		$request = $smcFunc['db_query']('', '
2657
			SHOW FULL COLUMNS
2658
			FROM {db_prefix}messages
2659
			LIKE {string:body_like}',
2660
			array(
2661
				'body_like' => 'body',
2662
			)
2663
		);
2664
		$column_info = $smcFunc['db_fetch_assoc']($request);
2665
		$smcFunc['db_free_result']($request);
2666
2667
		// A collation looks like latin1_swedish. We only need the character set.
2668
		list($upcontext['database_charset']) = explode('_', $column_info['Collation']);
2669
		$upcontext['database_charset'] = in_array($upcontext['database_charset'], $charsets) ? array_search($upcontext['database_charset'], $charsets) : $upcontext['database_charset'];
2670
2671
		// Detect whether a fulltext index is set.
2672
		$request = $smcFunc['db_query']('', '
2673
			SHOW INDEX
2674
			FROM {db_prefix}messages',
2675
			array(
2676
			)
2677
		);
2678
2679
		$upcontext['dropping_index'] = false;
2680
2681
		// If there's a fulltext index, we need to drop it first...
2682 View Code Duplication
		if ($request !== false || $smcFunc['db_num_rows']($request) != 0)
2683
		{
2684
			while ($row = $smcFunc['db_fetch_assoc']($request))
2685
				if ($row['Column_name'] == 'body' && (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT' || isset($row['Comment']) && $row['Comment'] == 'FULLTEXT'))
2686
					$upcontext['fulltext_index'][] = $row['Key_name'];
2687
			$smcFunc['db_free_result']($request);
2688
2689
			if (isset($upcontext['fulltext_index']))
2690
				$upcontext['fulltext_index'] = array_unique($upcontext['fulltext_index']);
2691
		}
2692
2693
		// Drop it and make a note...
2694
		if (!empty($upcontext['fulltext_index']))
2695
		{
2696
			$upcontext['dropping_index'] = true;
2697
2698
			$smcFunc['db_query']('', '
2699
			ALTER TABLE {db_prefix}messages
2700
			DROP INDEX ' . implode(',
2701
			DROP INDEX ', $upcontext['fulltext_index']),
2702
				array(
2703
					'db_error_skip' => true,
2704
				)
2705
			);
2706
2707
			// Update the settings table
2708
			$smcFunc['db_insert']('replace',
2709
				'{db_prefix}settings',
2710
				array('variable' => 'string', 'value' => 'string'),
2711
				array('db_search_index', ''),
2712
				array('variable')
2713
			);
2714
		}
2715
2716
		// Figure out what charset we should be converting from...
2717
		$lang_charsets = array(
2718
			'arabic' => 'windows-1256',
2719
			'armenian_east' => 'armscii-8',
2720
			'armenian_west' => 'armscii-8',
2721
			'azerbaijani_latin' => 'ISO-8859-9',
2722
			'bangla' => 'UTF-8',
2723
			'belarusian' => 'ISO-8859-5',
2724
			'bulgarian' => 'windows-1251',
2725
			'cambodian' => 'UTF-8',
2726
			'chinese_simplified' => 'gbk',
2727
			'chinese_traditional' => 'big5',
2728
			'croation' => 'ISO-8859-2',
2729
			'czech' => 'ISO-8859-2',
2730
			'czech_informal' => 'ISO-8859-2',
2731
			'english_pirate' => 'UTF-8',
2732
			'esperanto' => 'ISO-8859-3',
2733
			'estonian' => 'ISO-8859-15',
2734
			'filipino_tagalog' => 'UTF-8',
2735
			'filipino_vasayan' => 'UTF-8',
2736
			'georgian' => 'UTF-8',
2737
			'greek' => 'ISO-8859-3',
2738
			'hebrew' => 'windows-1255',
2739
			'hungarian' => 'ISO-8859-2',
2740
			'irish' => 'UTF-8',
2741
			'japanese' => 'UTF-8',
2742
			'khmer' => 'UTF-8',
2743
			'korean' => 'UTF-8',
2744
			'kurdish_kurmanji' => 'ISO-8859-9',
2745
			'kurdish_sorani' => 'windows-1256',
2746
			'lao' => 'tis-620',
2747
			'latvian' => 'ISO-8859-13',
2748
			'lithuanian' => 'ISO-8859-4',
2749
			'macedonian' => 'UTF-8',
2750
			'malayalam' => 'UTF-8',
2751
			'mongolian' => 'UTF-8',
2752
			'nepali' => 'UTF-8',
2753
			'persian' => 'UTF-8',
2754
			'polish' => 'ISO-8859-2',
2755
			'romanian' => 'ISO-8859-2',
2756
			'russian' => 'windows-1252',
2757
			'sakha' => 'UTF-8',
2758
			'serbian_cyrillic' => 'ISO-8859-5',
2759
			'serbian_latin' => 'ISO-8859-2',
2760
			'sinhala' => 'UTF-8',
2761
			'slovak' => 'ISO-8859-2',
2762
			'slovenian' => 'ISO-8859-2',
2763
			'telugu' => 'UTF-8',
2764
			'thai' => 'tis-620',
2765
			'turkish' => 'ISO-8859-9',
2766
			'turkmen' => 'ISO-8859-9',
2767
			'ukranian' => 'windows-1251',
2768
			'urdu' => 'UTF-8',
2769
			'uzbek_cyrillic' => 'ISO-8859-5',
2770
			'uzbek_latin' => 'ISO-8859-5',
2771
			'vietnamese' => 'UTF-8',
2772
			'yoruba' => 'UTF-8'
2773
		);
2774
2775
		// Default to ISO-8859-1 unless we detected another supported charset
2776
		$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';
2777
2778
		$upcontext['charset_list'] = array_keys($charsets);
2779
2780
		// Translation table for the character sets not native for MySQL.
2781
		$translation_tables = array(
2782
			'windows-1255' => array(
2783
				'0x81' => '\'\'',		'0x8A' => '\'\'',		'0x8C' => '\'\'',
2784
				'0x8D' => '\'\'',		'0x8E' => '\'\'',		'0x8F' => '\'\'',
2785
				'0x90' => '\'\'',		'0x9A' => '\'\'',		'0x9C' => '\'\'',
2786
				'0x9D' => '\'\'',		'0x9E' => '\'\'',		'0x9F' => '\'\'',
2787
				'0xCA' => '\'\'',		'0xD9' => '\'\'',		'0xDA' => '\'\'',
2788
				'0xDB' => '\'\'',		'0xDC' => '\'\'',		'0xDD' => '\'\'',
2789
				'0xDE' => '\'\'',		'0xDF' => '\'\'',		'0xFB' => '0xD792',
2790
				'0xFC' => '0xE282AC',		'0xFF' => '0xD6B2',		'0xC2' => '0xFF',
2791
				'0x80' => '0xFC',		'0xE2' => '0xFB',		'0xA0' => '0xC2A0',
2792
				'0xA1' => '0xC2A1',		'0xA2' => '0xC2A2',		'0xA3' => '0xC2A3',
2793
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2794
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2795
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2796
				'0xAF' => '0xC2AF',		'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',
2797
				'0xB2' => '0xC2B2',		'0xB3' => '0xC2B3',		'0xB4' => '0xC2B4',
2798
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2799
				'0xB8' => '0xC2B8',		'0xB9' => '0xC2B9',		'0xBB' => '0xC2BB',
2800
				'0xBC' => '0xC2BC',		'0xBD' => '0xC2BD',		'0xBE' => '0xC2BE',
2801
				'0xBF' => '0xC2BF',		'0xD7' => '0xD7B3',		'0xD1' => '0xD781',
2802
				'0xD4' => '0xD7B0',		'0xD5' => '0xD7B1',		'0xD6' => '0xD7B2',
2803
				'0xE0' => '0xD790',		'0xEA' => '0xD79A',		'0xEC' => '0xD79C',
2804
				'0xED' => '0xD79D',		'0xEE' => '0xD79E',		'0xEF' => '0xD79F',
2805
				'0xF0' => '0xD7A0',		'0xF1' => '0xD7A1',		'0xF2' => '0xD7A2',
2806
				'0xF3' => '0xD7A3',		'0xF5' => '0xD7A5',		'0xF6' => '0xD7A6',
2807
				'0xF7' => '0xD7A7',		'0xF8' => '0xD7A8',		'0xF9' => '0xD7A9',
2808
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2809
				'0x86' => '0xE280A0',	'0x87' => '0xE280A1',	'0x89' => '0xE280B0',
2810
				'0x8B' => '0xE280B9',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2811
				'0x95' => '0xE280A2',	'0x97' => '0xE28094',	'0x99' => '0xE284A2',
2812
				'0xC0' => '0xD6B0',		'0xC1' => '0xD6B1',		'0xC3' => '0xD6B3',
2813
				'0xC4' => '0xD6B4',		'0xC5' => '0xD6B5',		'0xC6' => '0xD6B6',
2814
				'0xC7' => '0xD6B7',		'0xC8' => '0xD6B8',		'0xC9' => '0xD6B9',
2815
				'0xCB' => '0xD6BB',		'0xCC' => '0xD6BC',		'0xCD' => '0xD6BD',
2816
				'0xCE' => '0xD6BE',		'0xCF' => '0xD6BF',		'0xD0' => '0xD780',
2817
				'0xD2' => '0xD782',		'0xE3' => '0xD793',		'0xE4' => '0xD794',
2818
				'0xE5' => '0xD795',		'0xE7' => '0xD797',		'0xE9' => '0xD799',
2819
				'0xFD' => '0xE2808E',	'0xFE' => '0xE2808F',	'0x92' => '0xE28099',
2820
				'0x83' => '0xC692',		'0xD3' => '0xD783',		'0x88' => '0xCB86',
2821
				'0x98' => '0xCB9C',		'0x91' => '0xE28098',	'0x96' => '0xE28093',
2822
				'0xBA' => '0xC3B7',		'0x9B' => '0xE280BA',	'0xAA' => '0xC397',
2823
				'0xA4' => '0xE282AA',	'0xE1' => '0xD791',		'0xE6' => '0xD796',
2824
				'0xE8' => '0xD798',		'0xEB' => '0xD79B',		'0xF4' => '0xD7A4',
2825
				'0xFA' => '0xD7AA',
2826
			),
2827
			'windows-1253' => array(
2828
				'0x81' => '\'\'',			'0x88' => '\'\'',			'0x8A' => '\'\'',
2829
				'0x8C' => '\'\'',			'0x8D' => '\'\'',			'0x8E' => '\'\'',
2830
				'0x8F' => '\'\'',			'0x90' => '\'\'',			'0x98' => '\'\'',
2831
				'0x9A' => '\'\'',			'0x9C' => '\'\'',			'0x9D' => '\'\'',
2832
				'0x9E' => '\'\'',			'0x9F' => '\'\'',			'0xAA' => '\'\'',
2833
				'0xD2' => '0xE282AC',			'0xFF' => '0xCE92',			'0xCE' => '0xCE9E',
2834
				'0xB8' => '0xCE88',		'0xBA' => '0xCE8A',		'0xBC' => '0xCE8C',
2835
				'0xBE' => '0xCE8E',		'0xBF' => '0xCE8F',		'0xC0' => '0xCE90',
2836
				'0xC8' => '0xCE98',		'0xCA' => '0xCE9A',		'0xCC' => '0xCE9C',
2837
				'0xCD' => '0xCE9D',		'0xCF' => '0xCE9F',		'0xDA' => '0xCEAA',
2838
				'0xE8' => '0xCEB8',		'0xEA' => '0xCEBA',		'0xEC' => '0xCEBC',
2839
				'0xEE' => '0xCEBE',		'0xEF' => '0xCEBF',		'0xC2' => '0xFF',
2840
				'0xBD' => '0xC2BD',		'0xED' => '0xCEBD',		'0xB2' => '0xC2B2',
2841
				'0xA0' => '0xC2A0',		'0xA3' => '0xC2A3',		'0xA4' => '0xC2A4',
2842
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2843
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2844
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2845
				'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',		'0xB3' => '0xC2B3',
2846
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2847
				'0xBB' => '0xC2BB',		'0xE2' => '0xCEB2',		'0x80' => '0xD2',
2848
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2849
				'0x86' => '0xE280A0',	'0xA1' => '0xCE85',		'0xA2' => '0xCE86',
2850
				'0x87' => '0xE280A1',	'0x89' => '0xE280B0',	'0xB9' => '0xCE89',
2851
				'0x8B' => '0xE280B9',	'0x91' => '0xE28098',	'0x99' => '0xE284A2',
2852
				'0x92' => '0xE28099',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2853
				'0x95' => '0xE280A2',	'0x96' => '0xE28093',	'0x97' => '0xE28094',
2854
				'0x9B' => '0xE280BA',	'0xAF' => '0xE28095',	'0xB4' => '0xCE84',
2855
				'0xC1' => '0xCE91',		'0xC3' => '0xCE93',		'0xC4' => '0xCE94',
2856
				'0xC5' => '0xCE95',		'0xC6' => '0xCE96',		'0x83' => '0xC692',
2857
				'0xC7' => '0xCE97',		'0xC9' => '0xCE99',		'0xCB' => '0xCE9B',
2858
				'0xD0' => '0xCEA0',		'0xD1' => '0xCEA1',		'0xD3' => '0xCEA3',
2859
				'0xD4' => '0xCEA4',		'0xD5' => '0xCEA5',		'0xD6' => '0xCEA6',
2860
				'0xD7' => '0xCEA7',		'0xD8' => '0xCEA8',		'0xD9' => '0xCEA9',
2861
				'0xDB' => '0xCEAB',		'0xDC' => '0xCEAC',		'0xDD' => '0xCEAD',
2862
				'0xDE' => '0xCEAE',		'0xDF' => '0xCEAF',		'0xE0' => '0xCEB0',
2863
				'0xE1' => '0xCEB1',		'0xE3' => '0xCEB3',		'0xE4' => '0xCEB4',
2864
				'0xE5' => '0xCEB5',		'0xE6' => '0xCEB6',		'0xE7' => '0xCEB7',
2865
				'0xE9' => '0xCEB9',		'0xEB' => '0xCEBB',		'0xF0' => '0xCF80',
2866
				'0xF1' => '0xCF81',		'0xF2' => '0xCF82',		'0xF3' => '0xCF83',
2867
				'0xF4' => '0xCF84',		'0xF5' => '0xCF85',		'0xF6' => '0xCF86',
2868
				'0xF7' => '0xCF87',		'0xF8' => '0xCF88',		'0xF9' => '0xCF89',
2869
				'0xFA' => '0xCF8A',		'0xFB' => '0xCF8B',		'0xFC' => '0xCF8C',
2870
				'0xFD' => '0xCF8D',		'0xFE' => '0xCF8E',
2871
			),
2872
		);
2873
2874
		// Make some preparations.
2875
		if (isset($translation_tables[$upcontext['charset_detected']]))
2876
		{
2877
			$replace = '%field%';
2878
2879
			// Build a huge REPLACE statement...
2880
			foreach ($translation_tables[$upcontext['charset_detected']] as $from => $to)
2881
				$replace = 'REPLACE(' . $replace . ', ' . $from . ', ' . $to . ')';
2882
		}
2883
2884
		// Get a list of table names ahead of time... This makes it easier to set our substep and such
2885
		db_extend();
2886
		$queryTables = $smcFunc['db_list_tables'](false, $db_prefix . '%');
2887
2888
		$upcontext['table_count'] = count($queryTables);
2889
2890
		// What ones have we already done?
2891 View Code Duplication
		foreach ($queryTables as $id => $table)
2892
			if ($id < $_GET['substep'])
2893
				$upcontext['previous_tables'][] = $table;
2894
2895
		$upcontext['cur_table_num'] = $_GET['substep'];
2896
		$upcontext['cur_table_name'] = str_replace($db_prefix, '', $queryTables[$_GET['substep']]);
2897
		$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2898
2899
		// Make sure we're ready & have painted the template before proceeding
2900
		if ($support_js && !isset($_GET['xml'])) {
2901
			$_GET['substep'] = 0;
2902
			return false;
2903
		}
2904
2905
		// We want to start at the first table.
2906
		for ($substep = $_GET['substep'], $n = count($queryTables); $substep < $n; $substep++)
2907
		{
2908
			$table = $queryTables[$substep];
2909
2910
			$getTableStatus = $smcFunc['db_query']('', '
2911
				SHOW TABLE STATUS
2912
				LIKE {string:table_name}',
2913
				array(
2914
					'table_name' => str_replace('_', '\_', $table)
2915
				)
2916
			);
2917
2918
			// Only one row so we can just fetch_assoc and free the result...
2919
			$table_info = $smcFunc['db_fetch_assoc']($getTableStatus);
2920
			$smcFunc['db_free_result']($getTableStatus);
2921
2922
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($queryTables[$substep + 1]) ? $queryTables[$substep + 1] : $queryTables[$substep]));
2923
			$upcontext['cur_table_num'] = $substep + 1;
2924
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2925
2926
			// Do we need to pause?
2927
			nextSubstep($substep);
2928
2929
			// Just to make sure it doesn't time out.
2930
			if (function_exists('apache_reset_timeout'))
2931
				@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...
2932
2933
			$table_charsets = array();
2934
2935
			// Loop through each column.
2936
			$queryColumns = $smcFunc['db_query']('', '
2937
				SHOW FULL COLUMNS
2938
				FROM ' . $table_info['Name'],
2939
				array(
2940
				)
2941
			);
2942
			while ($column_info = $smcFunc['db_fetch_assoc']($queryColumns))
2943
			{
2944
				// Only text'ish columns have a character set and need converting.
2945
				if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false)
2946
				{
2947
					$collation = empty($column_info['Collation']) || $column_info['Collation'] === 'NULL' ? $table_info['Collation'] : $column_info['Collation'];
2948
					if (!empty($collation) && $collation !== 'NULL')
2949
					{
2950
						list($charset) = explode('_', $collation);
2951
2952
						// Build structure of columns to operate on organized by charset; only operate on columns not yet utf8
2953
						if ($charset != 'utf8') {
2954
							if (!isset($table_charsets[$charset]))
2955
								$table_charsets[$charset] = array();
2956
2957
							$table_charsets[$charset][] = $column_info;
2958
						}
2959
					}
2960
				}
2961
			}
2962
			$smcFunc['db_free_result']($queryColumns);
2963
2964
			// Only change the non-utf8 columns identified above
2965
			if (count($table_charsets) > 0)
2966
			{
2967
				$updates_blob = '';
2968
				$updates_text = '';
2969
				foreach ($table_charsets as $charset => $columns)
2970
				{
2971
					if ($charset !== $charsets[$upcontext['charset_detected']])
2972
					{
2973
						foreach ($columns as $column)
2974
						{
2975
							$updates_blob .= '
2976
								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'] . '\'') . ',';
2977
							$updates_text .= '
2978
								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'] . '\'') . ',';
2979
						}
2980
					}
2981
				}
2982
2983
				// Change the columns to binary form.
2984
				$smcFunc['db_query']('', '
2985
					ALTER TABLE {raw:table_name}{raw:updates_blob}',
2986
					array(
2987
						'table_name' => $table_info['Name'],
2988
						'updates_blob' => substr($updates_blob, 0, -1),
2989
					)
2990
				);
2991
2992
				// Convert the character set if MySQL has no native support for it.
2993
				if (isset($translation_tables[$upcontext['charset_detected']]))
2994
				{
2995
					$update = '';
2996
					foreach ($table_charsets as $charset => $columns)
2997
						foreach ($columns as $column)
2998
							$update .= '
2999
								' . $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...
3000
3001
					$smcFunc['db_query']('', '
3002
						UPDATE {raw:table_name}
3003
						SET {raw:updates}',
3004
						array(
3005
							'table_name' => $table_info['Name'],
3006
							'updates' => substr($update, 0, -1),
3007
						)
3008
					);
3009
				}
3010
3011
				// Change the columns back, but with the proper character set.
3012
				$smcFunc['db_query']('', '
3013
					ALTER TABLE {raw:table_name}{raw:updates_text}',
3014
					array(
3015
						'table_name' => $table_info['Name'],
3016
						'updates_text' => substr($updates_text, 0, -1),
3017
					)
3018
				);
3019
			}
3020
3021
			// Now do the actual conversion (if still needed).
3022
			if ($charsets[$upcontext['charset_detected']] !== 'utf8')
3023
			{
3024
				if ($command_line)
3025
					echo 'Converting table ' . $table_info['Name'] . ' to UTF-8...';
3026
3027
				$smcFunc['db_query']('', '
3028
					ALTER TABLE {raw:table_name}
3029
					CONVERT TO CHARACTER SET utf8',
3030
					array(
3031
						'table_name' => $table_info['Name'],
3032
					)
3033
				);
3034
3035
				if ($command_line)
3036
					echo " done.\n";
3037
			}
3038
			// If this is XML to keep it nice for the user do one table at a time anyway!
3039
			if (isset($_GET['xml']) && $upcontext['cur_table_num'] < $upcontext['table_count'])
3040
				return upgradeExit();
3041
		}
3042
3043
		$prev_charset = empty($translation_tables[$upcontext['charset_detected']]) ? $charsets[$upcontext['charset_detected']] : $translation_tables[$upcontext['charset_detected']];
3044
3045
		$smcFunc['db_insert']('replace',
3046
			'{db_prefix}settings',
3047
			array('variable' => 'string', 'value' => 'string'),
3048
			array(array('global_character_set', 'UTF-8'), array('previousCharacterSet', $prev_charset)),
3049
			array('variable')
3050
		);
3051
3052
		// Store it in Settings.php too because it's needed before db connection.
3053
		// Hopefully this works...
3054
		require_once($sourcedir . '/Subs-Admin.php');
3055
		updateSettingsFile(array('db_character_set' => '\'utf8\''));
3056
3057
		// The conversion might have messed up some serialized strings. Fix them!
3058
		$request = $smcFunc['db_query']('', '
3059
			SELECT id_action, extra
3060
			FROM {db_prefix}log_actions
3061
			WHERE action IN ({string:remove}, {string:delete})',
3062
			array(
3063
				'remove' => 'remove',
3064
				'delete' => 'delete',
3065
			)
3066
		);
3067
		while ($row = $smcFunc['db_fetch_assoc']($request))
3068
		{
3069
			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)
3070
				$smcFunc['db_query']('', '
3071
					UPDATE {db_prefix}log_actions
3072
					SET extra = {string:extra}
3073
					WHERE id_action = {int:current_action}',
3074
					array(
3075
						'current_action' => $row['id_action'],
3076
						'extra' => $matches[1] . strlen($matches[3]) . ':"' . $matches[3] . '"' . $matches[4],
3077
					)
3078
				);
3079
		}
3080
		$smcFunc['db_free_result']($request);
3081
3082
		if ($upcontext['dropping_index'] && $command_line)
3083
		{
3084
			echo "\n" . '', $txt['upgrade_fulltext_error'] ,'';
3085
			flush();
3086
		}
3087
	}
3088
	$_GET['substep'] = 0;
3089
	return false;
3090
}
3091
3092
function serialize_to_json()
3093
{
3094
	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...
3095
3096
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'serialize_json_xml' : 'serialize_json';
3097
	// First thing's first - did we already do this?
3098
	if (!empty($modSettings['json_done']))
3099
	{
3100
		if ($command_line)
3101
			return DeleteUpgrade();
3102
		else
3103
			return true;
3104
	}
3105
3106
	// Done it already - js wise?
3107
	if (!empty($_POST['json_done']))
3108
		return true;
3109
3110
	// List of tables affected by this function
3111
	// 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...
3112
	// If 3rd item in array is true, it indicates that col1 could be empty...
3113
	$tables = array(
3114
		'background_tasks' => array('id_task', 'task_data'),
3115
		'log_actions' => array('id_action', 'extra'),
3116
		'log_online' => array('session', 'url'),
3117
		'log_packages' => array('id_install', 'db_changes', 'failed_steps', 'credits'),
3118
		'log_spider_hits' => array('id_hit', 'url'),
3119
		'log_subscribed' => array('id_sublog', 'pending_details'),
3120
		'pm_rules' => array('id_rule', 'criteria', 'actions'),
3121
		'qanda' => array('id_question', 'answers'),
3122
		'subscriptions' => array('id_subscribe', 'cost'),
3123
		'user_alerts' => array('id_alert', 'extra', true),
3124
		'user_drafts' => array('id_draft', 'to_list', true),
3125
		// These last two are a bit different - we'll handle those separately
3126
		'settings' => array(),
3127
		'themes' => array()
3128
	);
3129
3130
	// Set up some context stuff...
3131
	// Because we're not using numeric indices, we need this to figure out the current table name...
3132
	$keys = array_keys($tables);
3133
3134
	$upcontext['page_title'] = $txt['converting_json'];
3135
	$upcontext['table_count'] = count($keys);
3136
	$upcontext['cur_table_num'] = $_GET['substep'];
3137
	$upcontext['cur_table_name'] = isset($keys[$_GET['substep']]) ? $keys[$_GET['substep']] : $keys[0];
3138
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3139
3140 View Code Duplication
	foreach ($keys as $id => $table)
3141
		if ($id < $_GET['substep'])
3142
			$upcontext['previous_tables'][] = $table;
3143
3144
	if ($command_line)
3145
		echo 'Converting data from serialize() to json_encode().';
3146
3147
	if (!$support_js || isset($_GET['xml']))
3148
	{
3149
		// Fix the data in each table
3150
		for ($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
3151
		{
3152
			$upcontext['cur_table_name'] = isset($keys[$substep + 1]) ? $keys[$substep + 1] : $keys[$substep];
3153
			$upcontext['cur_table_num'] = $substep + 1;
3154
3155
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3156
3157
			// Do we need to pause?
3158
			nextSubstep($substep);
3159
3160
			// Initialize a few things...
3161
			$where = '';
3162
			$vars = array();
3163
			$table = $keys[$substep];
3164
			$info = $tables[$table];
3165
3166
			// Now the fun - build our queries and all that fun stuff
3167
			if ($table == 'settings')
3168
			{
3169
				// Now a few settings...
3170
				$serialized_settings = array(
3171
					'attachment_basedirectories',
3172
					'attachmentUploadDir',
3173
					'cal_today_birthday',
3174
					'cal_today_event',
3175
					'cal_today_holiday',
3176
					'displayFields',
3177
					'last_attachments_directory',
3178
					'memberlist_cache',
3179
					'search_custom_index_config',
3180
					'spider_name_cache'
3181
				);
3182
3183
				// Loop through and fix these...
3184
				$new_settings = array();
3185
				if ($command_line)
3186
					echo "\n" . 'Fixing some settings...';
3187
3188
				foreach ($serialized_settings as $var)
3189
				{
3190
					if (isset($modSettings[$var]))
3191
					{
3192
						// Attempt to unserialize the setting
3193
						$temp = @safe_unserialize($modSettings[$var]);
3194
						if (!$temp && $command_line)
3195
							echo "\n - Failed to unserialize the '" . $var . "' setting. Skipping.";
3196
						elseif ($temp !== false)
3197
							$new_settings[$var] = json_encode($temp);
3198
					}
3199
				}
3200
3201
				// Update everything at once
3202
				if (!function_exists('cache_put_data'))
3203
					require_once($sourcedir . '/Load.php');
3204
				updateSettings($new_settings, true);
3205
3206
				if ($command_line)
3207
					echo ' done.';
3208
			}
3209
			elseif ($table == 'themes')
3210
			{
3211
				// Finally, fix the admin prefs. Unfortunately this is stored per theme, but hopefully they only have one theme installed at this point...
3212
				$query = $smcFunc['db_query']('', '
3213
					SELECT id_member, id_theme, value FROM {db_prefix}themes
3214
					WHERE variable = {string:admin_prefs}',
3215
						array(
3216
							'admin_prefs' => 'admin_preferences'
3217
						)
3218
				);
3219
3220
				if ($smcFunc['db_num_rows']($query) != 0)
3221
				{
3222
					while ($row = $smcFunc['db_fetch_assoc']($query))
3223
					{
3224
						$temp = @safe_unserialize($row['value']);
3225
3226
						if ($command_line)
3227
						{
3228
							if ($temp === false)
3229
								echo "\n" . 'Unserialize of admin_preferences for user ' . $row['id_member'] . ' failed. Skipping.';
3230
							else
3231
								echo "\n" . 'Fixing admin preferences...';
3232
						}
3233
3234
						if ($temp !== false)
3235
						{
3236
							$row['value'] = json_encode($temp);
3237
3238
							// Even though we have all values from the table, UPDATE is still faster than REPLACE
3239
							$smcFunc['db_query']('', '
3240
								UPDATE {db_prefix}themes
3241
								SET value = {string:prefs}
3242
								WHERE id_theme = {int:theme}
3243
									AND id_member = {int:member}
3244
									AND variable = {string:admin_prefs}',
3245
								array(
3246
									'prefs' => $row['value'],
3247
									'theme' => $row['id_theme'],
3248
									'member' => $row['id_member'],
3249
									'admin_prefs' => 'admin_preferences'
3250
								)
3251
							);
3252
3253
							if ($command_line)
3254
								echo ' done.';
3255
						}
3256
					}
3257
3258
					$smcFunc['db_free_result']($query);
3259
				}
3260
			}
3261
			else
3262
			{
3263
				// First item is always the key...
3264
				$key = $info[0];
3265
				unset($info[0]);
3266
3267
				// Now we know what columns we have and such...
3268
				if (count($info) == 2 && $info[2] === true)
3269
				{
3270
					$col_select = $info[1];
3271
					$where = ' WHERE ' . $info[1] . ' != {empty}';
3272
				}
3273
				else
3274
				{
3275
					$col_select = implode(', ', $info);
3276
				}
3277
3278
				$query = $smcFunc['db_query']('', '
3279
					SELECT ' . $key . ', ' . $col_select . '
3280
					FROM {db_prefix}' . $table . $where,
3281
					array()
3282
				);
3283
3284
				if ($smcFunc['db_num_rows']($query) != 0)
3285
				{
3286
					if ($command_line)
3287
					{
3288
						echo "\n" . ' +++ Fixing the "' . $table . '" table...';
3289
						flush();
3290
					}
3291
3292
					while ($row = $smcFunc['db_fetch_assoc']($query))
3293
					{
3294
						$update = '';
3295
3296
						// We already know what our key is...
3297
						foreach ($info as $col)
3298
						{
3299
							if ($col !== true && $row[$col] != '')
3300
							{
3301
								$temp = @safe_unserialize($row[$col]);
3302
3303
								if ($temp === false && $command_line)
3304
								{
3305
									echo "\nFailed to unserialize " . $row[$col] . "... Skipping\n";
3306
								}
3307
								else
3308
								{
3309
									$row[$col] = json_encode($temp);
3310
3311
									// Build our SET string and variables array
3312
									$update .= (empty($update) ? '' : ', ') . $col . ' = {string:' . $col . '}';
3313
									$vars[$col] = $row[$col];
3314
								}
3315
							}
3316
						}
3317
3318
						$vars[$key] = $row[$key];
3319
3320
						// In a few cases, we might have empty data, so don't try to update in those situations...
3321
						if (!empty($update))
3322
						{
3323
							$smcFunc['db_query']('', '
3324
								UPDATE {db_prefix}' . $table . '
3325
								SET ' . $update . '
3326
								WHERE ' . $key . ' = {' . ($key == 'session' ? 'string' : 'int') . ':' . $key . '}',
3327
								$vars
3328
							);
3329
						}
3330
					}
3331
3332
					if ($command_line)
3333
						echo ' done.';
3334
3335
					// Free up some memory...
3336
					$smcFunc['db_free_result']($query);
3337
				}
3338
			}
3339
			// If this is XML to keep it nice for the user do one table at a time anyway!
3340
			if (isset($_GET['xml']))
3341
				return upgradeExit();
3342
		}
3343
3344
		if ($command_line)
3345
		{
3346
			echo "\n" . 'Successful.' . "\n";
3347
			flush();
3348
		}
3349
		$upcontext['step_progress'] = 100;
3350
3351
		// Last but not least, insert a dummy setting so we don't have to do this again in the future...
3352
		updateSettings(array('json_done' => true));
3353
3354
		$_GET['substep'] = 0;
3355
		// Make sure we move on!
3356
		if ($command_line)
3357
			return DeleteUpgrade();
3358
3359
		return true;
3360
	}
3361
3362
	// If this fails we just move on to deleting the upgrade anyway...
3363
	$_GET['substep'] = 0;
3364
	return false;
3365
}
3366
3367
/**
3368
 * As of 2.1, we want to store db_last_error.php in the cache
3369
 * To make that happen, Settings.php needs to ensure the $cachedir path is correct before trying to write to db_last_error.php
3370
 */
3371
function move_db_last_error_to_cachedir()
3372
{
3373
	$settings = file_get_contents(dirname(__FILE__) . '/Settings.php');
3374
3375
	$regex = <<<'EOT'
3376
(\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';
3377
EOT;
3378
3379
	$replacement = <<<'EOT'
3380
# Make sure the paths are correct... at least try to fix them.
3381
if (!file_exists($boarddir) && file_exists(dirname(__FILE__) . '/agreement.txt'))
3382
	$boarddir = dirname(__FILE__);
3383
if (!file_exists($sourcedir) && file_exists($boarddir . '/Sources'))
3384
	$sourcedir = $boarddir . '/Sources';
3385
if (!file_exists($cachedir) && file_exists($boarddir . '/cache'))
3386
	$cachedir = $boarddir . '/cache';
3387
3388
3389
EOT;
3390
3391
	if (preg_match('~' . $regex . '~', $settings) && preg_match('~(#+\s*Error-Catching\s*#+)~', $settings))
3392
	{
3393
		$settings = preg_replace('~' . $regex . '~', '', $settings);
3394
		$settings = preg_replace('~(#+\s*Error-Catching\s*#+)~', $replacement . '$1', $settings);
3395
		$settings = preg_replace('~dirname(__FILE__) . \'/db_last_error.php\'~', '(isset($cachedir) ? $cachedir : dirname(__FILE__)) . \'/db_last_error.php\'', $settings);
3396
3397
		// Blank out the file - done to fix a oddity with some servers.
3398
		file_put_contents(dirname(__FILE__) . '/Settings.php', '');
3399
3400
		file_put_contents(dirname(__FILE__) . '/Settings.php', $settings);
3401
	}
3402
}
3403
3404
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3405
                        Templates are below this point
3406
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
3407
3408
// This is what is displayed if there's any chmod to be done. If not it returns nothing...
3409
function template_chmod()
3410
{
3411
	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...
3412
3413
	// Don't call me twice!
3414
	if (!empty($upcontext['chmod_called']))
3415
		return;
3416
3417
	$upcontext['chmod_called'] = true;
3418
3419
	// Nothing?
3420
	if (empty($upcontext['chmod']['files']) && empty($upcontext['chmod']['ftp_error']))
3421
		return;
3422
3423
	// Was it a problem with Windows?
3424
	if (!empty($upcontext['chmod']['ftp_error']) && $upcontext['chmod']['ftp_error'] == 'total_mess')
3425
	{
3426
		echo '
3427
		<div class="error">
3428
			<p>', $txt['upgrade_writable_files'] ,'</p>
3429
			<ul class="error_content">
3430
				<li>' . implode('</li>
3431
				<li>', $upcontext['chmod']['files']) . '</li>
3432
			</ul>
3433
		</div>';
3434
3435
		return false;
3436
	}
3437
3438
	echo '
3439
		<div class="panel">
3440
			<h2>', $txt['upgrade_ftp_login'], '</h2>
3441
			<h3>', $txt['upgrade_ftp_perms'], '</h3>
3442
			<script>
3443
				function warning_popup()
3444
				{
3445
					popup = window.open(\'\',\'popup\',\'height=150,width=400,scrollbars=yes\');
3446
					var content = popup.document;
3447
					content.write(\'<!DOCTYPE html>\n\');
3448
					content.write(\'<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>\n\t<head>\n\t\t<meta name="robots" content="noindex">\n\t\t\');
3449
					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\');
3450
					content.write(\'<div class="windowbg description">\n\t\t\t<h4>', $txt['upgrade_ftp_files'], '</h4>\n\t\t\t\');
3451
					content.write(\'<p>', implode('<br>\n\t\t\t', $upcontext['chmod']['files']), '</p>\n\t\t\t\');';
3452
3453
	if (isset($upcontext['systemos']) && $upcontext['systemos'] == 'linux')
3454
		echo '
3455
					content.write(\'<hr>\n\t\t\t\');
3456
					content.write(\'<p>', $txt['upgrade_ftp_shell'], '</p>\n\t\t\t\');
3457
					content.write(\'<tt># chmod a+w ', implode(' ', $upcontext['chmod']['files']), '</tt>\n\t\t\t\');';
3458
3459
	echo '
3460
					content.write(\'<a href="javascript:self.close();">close</a>\n\t\t</div>\n\t</body>\n</html>\');
3461
					content.close();
3462
				}
3463
			</script>';
3464
3465
	if (!empty($upcontext['chmod']['ftp_error']))
3466
		echo '
3467
			<div class="error_message red">
3468
				<p>', $txt['upgrade_ftp_error'], '<p>
3469
				<code>', $upcontext['chmod']['ftp_error'], '</code>
3470
			</div>';
3471
3472
	if (empty($upcontext['chmod_in_form']))
3473
		echo '
3474
			<form action="', $upcontext['form_url'], '" method="post">';
3475
3476
	echo '
3477
				<dl class="settings">
3478
					<dt>
3479
						<label for="ftp_server">', $txt['ftp_server'], ':</label>
3480
					</dt>
3481
					<dd>
3482
						<div class="floatright">
3483
							<label for="ftp_port" class="textbox"><strong>', $txt['ftp_port'], ':</strong></label>
3484
							<input type="text" size="3" name="ftp_port" id="ftp_port" value="', isset($upcontext['chmod']['port']) ? $upcontext['chmod']['port'] : '21', '">
3485
						</div>
3486
						<input type="text" size="30" name="ftp_server" id="ftp_server" value="', isset($upcontext['chmod']['server']) ? $upcontext['chmod']['server'] : 'localhost', '">
3487
						<div class="smalltext">', $txt['ftp_server_info'], '</div>
3488
					</dd>
3489
					<dt>
3490
						<label for="ftp_username">', $txt['ftp_username'], ':</label>
3491
					</dt>
3492
					<dd>
3493
						<input type="text" size="30" name="ftp_username" id="ftp_username" value="', isset($upcontext['chmod']['username']) ? $upcontext['chmod']['username'] : '', '">
3494
						<div class="smalltext">', $txt['ftp_username_info'], '</div>
3495
					</dd>
3496
					<dt>
3497
						<label for="ftp_password">', $txt['ftp_password'], ':</label>
3498
					</dt>
3499
					<dd>
3500
						<input type="password" size="30" name="ftp_password" id="ftp_password">
3501
						<div class="smalltext">', $txt['ftp_password_info'], '</div>
3502
					</dd>
3503
					<dt>
3504
						<label for="ftp_path">', $txt['ftp_path'], ':</label>
3505
					</dt>
3506
					<dd>
3507
						<input type="text" size="30" name="ftp_path" id="ftp_path" value="', isset($upcontext['chmod']['path']) ? $upcontext['chmod']['path'] : '', '">
3508
						<div class="smalltext">', !empty($upcontext['chmod']['path']) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'], '</div>
3509
					</dd>
3510
				</dl>
3511
3512
				<div class="righttext buttons">
3513
					<input type="submit" value="', $txt['ftp_connect'], '" class="button">
3514
				</div>';
3515
3516
	if (empty($upcontext['chmod_in_form']))
3517
		echo '
3518
			</form>';
3519
3520
	echo '
3521
		</div><!-- .panel -->';
3522
}
3523
3524
function template_upgrade_above()
3525
{
3526
	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...
3527
3528
	echo '<!DOCTYPE html>
3529
<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>
3530
<head>
3531
	<meta charset="', isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8', '">
3532
	<meta name="robots" content="noindex">
3533
	<title>', $txt['upgrade_upgrade_utility'], '</title>
3534
	<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/index.css?alp21">
3535
	<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/install.css?alp21">
3536
	', $txt['lang_rtl'] == true ? '<link rel="stylesheet" href="' . $settings['default_theme_url'] . '/css/rtl.css?alp21">' : '', '
3537
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
3538
	<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
3539
	<script>
3540
		var smf_scripturl = \'', $upgradeurl, '\';
3541
		var smf_charset = \'', (empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'UTF-8' : $txt['lang_character_set']) : $modSettings['global_character_set']), '\';
3542
		var startPercent = ', $upcontext['overall_percent'], ';
3543
3544
		// This function dynamically updates the step progress bar - and overall one as required.
3545
		function updateStepProgress(current, max, overall_weight)
3546
		{
3547
			// What out the actual percent.
3548
			var width = parseInt((current / max) * 100);
3549
			if (document.getElementById(\'step_progress\'))
3550
			{
3551
				document.getElementById(\'step_progress\').style.width = width + "%";
3552
				setInnerHTML(document.getElementById(\'step_text\'), width + "%");
3553
			}
3554
			if (overall_weight && document.getElementById(\'overall_progress\'))
3555
			{
3556
				overall_width = parseInt(startPercent + width * (overall_weight / 100));
3557
				document.getElementById(\'overall_progress\').style.width = overall_width + "%";
3558
				setInnerHTML(document.getElementById(\'overall_text\'), overall_width + "%");
3559
			}
3560
		}
3561
	</script>
3562
</head>
3563
<body>
3564
	<div id="footerfix">
3565
	<div id="header">
3566
		<h1 class="forumtitle">', $txt['upgrade_upgrade_utility'], '</h1>
3567
		<img id="smflogo" src="', $settings['default_theme_url'], '/images/smflogo.svg" alt="Simple Machines Forum" title="Simple Machines Forum">
3568
	</div>
3569
	<div id="wrapper">
3570
		<div id="upper_section">
3571
			<div id="inner_section">
3572
				<div id="inner_wrap">
3573
				</div>
3574
			</div>
3575
		</div>
3576
		<div id="content_section">
3577
			<div id="main_content_section">
3578
				<div id="main_steps">
3579
					<h2>', $txt['upgrade_progress'], '</h2>
3580
					<ul>';
3581
3582 View Code Duplication
	foreach ($upcontext['steps'] as $num => $step)
3583
		echo '
3584
						<li class="', $num < $upcontext['current_step'] ? 'stepdone' : ($num == $upcontext['current_step'] ? 'stepcurrent' : 'stepwaiting'), '">', $txt['upgrade_step'], ' ', $step[0], ': ', $step[1], '</li>';
3585
3586
	echo '
3587
					</ul>
3588
				</div><!-- #main_steps -->
3589
				
3590
				<div id="install_progress">
3591
					<div id="progress_bar" class="progress_bar progress_green">
3592
						<h3>', $txt['upgrade_overall_progress'], '</h3>
3593
						<div id="overall_progress" class="bar" style="width: ', $upcontext['overall_percent'], '%;"></div>
3594
						<span id="overall_text">', $upcontext['overall_percent'], '%</span>
3595
					</div>';
3596
3597
	if (isset($upcontext['step_progress']))
3598
		echo '
3599
					<div id="progress_bar_step" class="progress_bar progress_yellow">
3600
						<h3>', $txt['upgrade_step_progress'], '</h3>
3601
						<div id="step_progress" class="bar" style="width: ', $upcontext['step_progress'], '%;"></div>
3602
						<span id="step_text">', $upcontext['step_progress'], '%</span>
3603
					</div>';
3604
3605
	echo '
3606
					<div id="substep_bar_div" class="progress_bar" style="display: ', isset($upcontext['substep_progress']) ? '' : 'none', ';">
3607
						<h3 id="substep_name">', isset($upcontext['substep_progress_name']) ? trim(strtr($upcontext['substep_progress_name'], array('.' => ''))) : '', '</h3>
3608
						<div id="substep_progress" class="bar" style="width: ', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%;"></div>
3609
						<span id="substep_text">', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%</span>
3610
					</div>';
3611
3612
	// How long have we been running this?
3613
	$elapsed = time() - $upcontext['started'];
3614
	$mins = (int) ($elapsed / 60);
3615
	$seconds = $elapsed - $mins * 60;
3616
	echo '
3617
					<div class="smalltext time_elapsed">
3618
						', $txt['upgrade_time_elapsed'], ':
3619
						<span id="mins_elapsed">', $mins, '</span> ', $txt['upgrade_time_mins'], ', <span id="secs_elapsed">', $seconds, '</span> ', $txt['upgrade_time_secs'], '.
3620
					</div>';
3621
	echo '
3622
				</div><!-- #install_progress -->
3623
			</div><!-- #main_content_section -->
3624
		</div><!-- #content_section -->
3625
		<div id="main_screen" class="clear">
3626
			<h2>', $upcontext['page_title'], '</h2>
3627
			<div class="panel">';
3628
}
3629
3630
function template_upgrade_below()
3631
{
3632
	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...
3633
3634
	if (!empty($upcontext['pause']))
3635
		echo '
3636
					<em>', $txt['upgrade_incomplete'], '.</em><br>
3637
3638
					<h2 style="margin-top: 2ex;">', $txt['upgrade_not_quite_done'], '</h2>
3639
					<h3>
3640
						', $txt['upgrade_paused_overload'], '
3641
					</h3>';
3642
3643
	if (!empty($upcontext['custom_warning']))
3644
		echo '
3645
					<div class="errorbox">
3646
						<h3>', $txt['upgrade_note'], '</h3>
3647
						', $upcontext['custom_warning'], '
3648
					</div>';
3649
3650
	echo '
3651
					<div class="righttext" style="margin: 1ex;">';
3652
3653
	if (!empty($upcontext['continue']))
3654
		echo '
3655
						<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '"', $upcontext['continue'] == 2 ? ' disabled' : '', ' class="button">';
3656
	if (!empty($upcontext['skip']))
3657
		echo '
3658
						<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="dontSubmit = true; document.getElementById(\'contbutt\').disabled = \'disabled\'; return true;" class="button">';
3659
3660
	echo '
3661
					</div>
3662
				</form>
3663
			</div><!-- .panel -->
3664
		</div><!-- #main_screen -->
3665
	</div><!-- #wrapper -->
3666
	</div><!-- #footerfix -->
3667
	<div id="footer">
3668
		<ul>
3669
			<li class="copyright"><a href="https://www.simplemachines.org/" title="Simple Machines Forum" target="_blank" rel="noopener">SMF &copy; 2018, Simple Machines</a></li>
3670
		</ul>
3671
	</div>';
3672
3673
	// Are we on a pause?
3674
	if (!empty($upcontext['pause']))
3675
	{
3676
		echo '
3677
	<script>
3678
		window.onload = doAutoSubmit;
3679
		var countdown = 3;
3680
		var dontSubmit = false;
3681
3682
		function doAutoSubmit()
3683
		{
3684
			if (countdown == 0 && !dontSubmit)
3685
				document.upform.submit();
3686
			else if (countdown == -1)
3687
				return;
3688
3689
			document.getElementById(\'contbutt\').value = "', $txt['upgrade_continue'], ' (" + countdown + ")";
3690
			countdown--;
3691
3692
			setTimeout("doAutoSubmit();", 1000);
3693
		}
3694
	</script>';
3695
	}
3696
3697
	echo '
3698
</body>
3699
</html>';
3700
}
3701
3702
function template_xml_above()
3703
{
3704
	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...
3705
3706
	echo '<', '?xml version="1.0" encoding="UTF-8"?', '>
3707
	<smf>';
3708
3709
	if (!empty($upcontext['get_data']))
3710
		foreach ($upcontext['get_data'] as $k => $v)
3711
			echo '
3712
		<get key="', $k, '">', $v, '</get>';
3713
}
3714
3715
function template_xml_below()
3716
{
3717
	echo '
3718
	</smf>';
3719
}
3720
3721
function template_error_message()
3722
{
3723
	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...
3724
3725
	echo '
3726
	<div class="error_message red">
3727
		', $upcontext['error_msg'], '
3728
		<br>
3729
		<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 3729

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