Completed
Push — release-2.1 ( 4466de...b69d19 )
by Michael
25s
created

upgrade.php ➔ template_convert_xml()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines http://www.simplemachines.org
8
 * @copyright 2017 Simple Machines and individual contributors
9
 * @license http://www.simplemachines.org/about/smf/license.php BSD
10
 *
11
 * @version 2.1 Beta 3
12
 */
13
14
// Version information...
15
define('SMF_VERSION', '2.1 Beta 3');
16
define('SMF_LANG_VERSION', '2.1 Beta 3');
17
18
/**
19
 * The minimum required PHP version.
20
 * @var string
21
 */
22
$GLOBALS['required_php_version'] = '5.3.8';
23
24
/**
25
 * A list of supported database systems.
26
 * @var array
27
 */
28
$databases = array(
29
	'mysql' => array(
30
		'name' => 'MySQL',
31
		'version' => '5.0.3',
32
		'version_check' => 'global $db_connection; return min(mysqli_get_server_info($db_connection), mysqli_get_client_info());',
33
		'utf8_support' => true,
34
		'utf8_version' => '5.0.3',
35
		'utf8_version_check' => 'global $db_connection; return mysqli_get_server_info($db_connection);',
36
		'alter_support' => true,
37
	),
38
	'postgresql' => array(
39
		'name' => 'PostgreSQL',
40
		'version' => '9.1',
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 = 30;
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
 * The base URL for the external SMF resources.
66
 * @var string
67
 */
68
$smfsite = 'http://www.simplemachines.org/smf';
69
70
/**
71
 * Flag to disable the required administrator login.
72
 * @var bool
73
 */
74
$disable_security = false;
75
76
/**
77
 * The amount of seconds allowed between logins.
78
 * If the first user to login is inactive for this amount of seconds, a second login is allowed.
79
 * @var int
80
 */
81
$upcontext['inactive_timeout'] = 10;
82
83
// The helper is crucial. Include it first thing.
84
if (!file_exists($upgrade_path . '/upgrade-helper.php'))
85
    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.');
86
87
require_once($upgrade_path . '/upgrade-helper.php');
88
89
// All the steps in detail.
90
// Number,Name,Function,Progress Weight.
91
$upcontext['steps'] = array(
92
	0 => array(1, 'Login', 'WelcomeLogin', 2),
93
	1 => array(2, 'Upgrade Options', 'UpgradeOptions', 2),
94
	2 => array(3, 'Backup', 'BackupDatabase', 10),
95
	3 => array(4, 'Database Changes', 'DatabaseChanges', 50),
96
	4 => array(5, 'Convert to UTF-8', 'ConvertUtf8', 20),
97
	5 => array(6, 'Convert serialized strings to JSON', 'serialize_to_json', 10),
98
	6 => array(7, 'Delete Upgrade.php', 'DeleteUpgrade', 1),
99
);
100
// Just to remember which one has files in it.
101
$upcontext['database_step'] = 3;
102
@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...
103
if (!ini_get('safe_mode'))
104
{
105
	ini_set('mysql.connect_timeout', -1);
106
	ini_set('default_socket_timeout', 900);
107
}
108
// Clean the upgrade path if this is from the client.
109
if (!empty($_SERVER['argv']) && php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
110
	for ($i = 1; $i < $_SERVER['argc']; $i++)
111
	{
112
		if (preg_match('~^--path=(.+)$~', $_SERVER['argv'][$i], $match) != 0)
113
			$upgrade_path = substr($match[1], -1) == '/' ? substr($match[1], 0, -1) : $match[1];
114
	}
115
116
// Are we from the client?
117
if (php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
118
{
119
	$command_line = true;
120
	$disable_security = true;
121
}
122
else
123
	$command_line = false;
124
125
// Load this now just because we can.
126
require_once($upgrade_path . '/Settings.php');
127
128
// We don't use "-utf8" anymore...  Tweak the entry that may have been loaded by Settings.php
129
if (isset($language))
130
	$language = str_ireplace('-utf8', '', $language);
131
132
// Are we logged in?
133
if (isset($upgradeData))
134
{
135
	$upcontext['user'] = json_decode(base64_decode($upgradeData), true);
136
137
	// Check for sensible values.
138 View Code Duplication
	if (empty($upcontext['user']['started']) || $upcontext['user']['started'] < time() - 86400)
139
		$upcontext['user']['started'] = time();
140 View Code Duplication
	if (empty($upcontext['user']['updated']) || $upcontext['user']['updated'] < time() - 86400)
141
		$upcontext['user']['updated'] = 0;
142
143
	$upcontext['started'] = $upcontext['user']['started'];
144
	$upcontext['updated'] = $upcontext['user']['updated'];
145
146
	$is_debug = !empty($upcontext['user']['debug']) ? true : false;
147
}
148
149
// Nothing sensible?
150
if (empty($upcontext['updated']))
151
{
152
	$upcontext['started'] = time();
153
	$upcontext['updated'] = 0;
154
	$upcontext['user'] = array(
155
		'id' => 0,
156
		'name' => 'Guest',
157
		'pass' => 0,
158
		'started' => $upcontext['started'],
159
		'updated' => $upcontext['updated'],
160
	);
161
}
162
163
// Load up some essential data...
164
loadEssentialData();
165
166
// Are we going to be mimic'ing SSI at this point?
167
if (isset($_GET['ssi']))
168
{
169
	require_once($sourcedir . '/Errors.php');
170
	require_once($sourcedir . '/Logging.php');
171
	require_once($sourcedir . '/Load.php');
172
	require_once($sourcedir . '/Security.php');
173
	require_once($sourcedir . '/Subs-Package.php');
174
175
	loadUserSettings();
176
	loadPermissions();
177
}
178
179
// Include our helper functions.
180
require_once($sourcedir . '/Subs.php');
181
require_once($sourcedir . '/LogInOut.php');
182
183
// This only exists if we're on SMF ;)
184
if (isset($modSettings['smfVersion']))
185
{
186
	$request = $smcFunc['db_query']('', '
187
		SELECT variable, value
188
		FROM {db_prefix}themes
189
		WHERE id_theme = {int:id_theme}
190
			AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
191
		array(
192
			'id_theme' => 1,
193
			'theme_url' => 'theme_url',
194
			'theme_dir' => 'theme_dir',
195
			'images_url' => 'images_url',
196
			'db_error_skip' => true,
197
		)
198
	);
199 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
200
		$modSettings[$row['variable']] = $row['value'];
201
	$smcFunc['db_free_result']($request);
202
}
203
204
if (!isset($modSettings['theme_url']))
205
{
206
	$modSettings['theme_dir'] = $boarddir . '/Themes/default';
207
	$modSettings['theme_url'] = 'Themes/default';
208
	$modSettings['images_url'] = 'Themes/default/images';
209
}
210
if (!isset($settings['default_theme_url']))
211
	$settings['default_theme_url'] = $modSettings['theme_url'];
212
if (!isset($settings['default_theme_dir']))
213
	$settings['default_theme_dir'] = $modSettings['theme_dir'];
214
215
$upcontext['is_large_forum'] = (empty($modSettings['smfVersion']) || $modSettings['smfVersion'] <= '1.1 RC1') && !empty($modSettings['totalMessages']) && $modSettings['totalMessages'] > 75000;
216
// Default title...
217
$upcontext['page_title'] = 'Updating Your SMF Installation!';
218
219
// Have we got tracking data - if so use it (It will be clean!)
220
if (isset($_GET['data']))
221
{
222
	global $is_debug;
223
224
	$upcontext['upgrade_status'] = json_decode(base64_decode($_GET['data']), true);
225
	$upcontext['current_step'] = $upcontext['upgrade_status']['curstep'];
226
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
227
	$upcontext['rid'] = $upcontext['upgrade_status']['rid'];
228
	$support_js = $upcontext['upgrade_status']['js'];
229
230
	// Only set this if the upgrader status says so.
231
	if (empty($is_debug))
232
		$is_debug = $upcontext['upgrade_status']['debug'];
233
234
	// Load the language.
235
	if (file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
236
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
237
}
238
// Set the defaults.
239
else
240
{
241
	$upcontext['current_step'] = 0;
242
	$upcontext['rid'] = mt_rand(0, 5000);
243
	$upcontext['upgrade_status'] = array(
244
		'curstep' => 0,
245
		'lang' => isset($_GET['lang']) ? $_GET['lang'] : basename($language, '.lng'),
246
		'rid' => $upcontext['rid'],
247
		'pass' => 0,
248
		'debug' => 0,
249
		'js' => 0,
250
	);
251
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
252
}
253
254
// If this isn't the first stage see whether they are logging in and resuming.
255
if ($upcontext['current_step'] != 0 || !empty($upcontext['user']['step']))
256
	checkLogin();
257
258
if ($command_line)
259
	cmdStep0();
260
261
// Don't error if we're using xml.
262
if (isset($_GET['xml']))
263
	$upcontext['return_error'] = true;
264
265
// Loop through all the steps doing each one as required.
266
$upcontext['overall_percent'] = 0;
267
foreach ($upcontext['steps'] as $num => $step)
268
{
269
	if ($num >= $upcontext['current_step'])
270
	{
271
		// The current weight of this step in terms of overall progress.
272
		$upcontext['step_weight'] = $step[3];
273
		// Make sure we reset the skip button.
274
		$upcontext['skip'] = false;
275
276
		// We cannot proceed if we're not logged in.
277
		if ($num != 0 && !$disable_security && $upcontext['user']['pass'] != $upcontext['upgrade_status']['pass'])
278
		{
279
			$upcontext['steps'][0][2]();
280
			break;
281
		}
282
283
		// Call the step and if it returns false that means pause!
284
		if (function_exists($step[2]) && $step[2]() === false)
285
			break;
286
		elseif (function_exists($step[2])) {
287
			//Start each new step with this unset, so the 'normal' template is called first
288
			unset($_GET['xml']);
289
			$_GET['substep'] = 0;
290
			$upcontext['current_step']++;
291
		}
292
	}
293
	$upcontext['overall_percent'] += $step[3];
294
}
295
296
upgradeExit();
297
298
// Exit the upgrade script.
299
function upgradeExit($fallThrough = false)
300
{
301
	global $upcontext, $upgradeurl, $sourcedir, $command_line, $is_debug;
302
303
	// Save where we are...
304
	if (!empty($upcontext['current_step']) && !empty($upcontext['user']['id']))
305
	{
306
		$upcontext['user']['step'] = $upcontext['current_step'];
307
		$upcontext['user']['substep'] = $_GET['substep'];
308
		$upcontext['user']['updated'] = time();
309
		$upcontext['debug'] = $is_debug;
310
		$upgradeData = base64_encode(json_encode($upcontext['user']));
311
		require_once($sourcedir . '/Subs-Admin.php');
312
		updateSettingsFile(array('upgradeData' => '"' . $upgradeData . '"'));
313
		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...
314
	}
315
316
	// Handle the progress of the step, if any.
317
	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
318
	{
319
		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
320
		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
321
	}
322
	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
323
324
	// We usually dump our templates out.
325
	if (!$fallThrough)
326
	{
327
		// This should not happen my dear... HELP ME DEVELOPERS!!
328
		if (!empty($command_line))
329
		{
330
			if (function_exists('debug_print_backtrace'))
331
				debug_print_backtrace();
332
333
			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 307
  2. $upcontext is assigned
    in other/upgrade.php on line 308
  3. $upcontext is assigned
    in other/upgrade.php on line 309
  4. $upcontext is assigned
    in other/upgrade.php on line 322

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...
334
			flush();
335
			die();
336
		}
337
338
		if (!isset($_GET['xml']))
339
			template_upgrade_above();
340
		else
341
		{
342
			header('Content-Type: text/xml; charset=UTF-8');
343
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
344
			$upcontext['get_data'] = array();
345
			foreach ($_GET as $k => $v)
346
			{
347
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
348
				{
349
					$upcontext['get_data'][$k] = $v;
350
				}
351
			}
352
			template_xml_above();
353
		}
354
355
		// Call the template.
356
		if (isset($upcontext['sub_template']))
357
		{
358
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
359
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(json_encode($upcontext['upgrade_status']));
360
361
			// Custom stuff to pass back?
362
			if (!empty($upcontext['query_string']))
363
				$upcontext['form_url'] .= $upcontext['query_string'];
364
365
			// Call the appropriate subtemplate
366
			if (is_callable('template_' . $upcontext['sub_template']))
367
				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 307
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 307
  2. $upcontext is assigned
    in other/upgrade.php on line 308
  3. $upcontext is assigned
    in other/upgrade.php on line 309
  4. $upcontext is assigned
    in other/upgrade.php on line 322
  5. $upcontext is assigned
    in other/upgrade.php on line 358
  6. $upcontext is assigned
    in other/upgrade.php on line 359
  2. Path: Read from $_GET, and $v is assigned in other/upgrade.php on line 345
  1. Read from $_GET, and $v is assigned
    in other/upgrade.php on line 345
  2. $upcontext is assigned
    in other/upgrade.php on line 349
  3. $upcontext is assigned
    in other/upgrade.php on line 358
  4. $upcontext is assigned
    in other/upgrade.php on line 359
  3. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 359
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 359

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...
368
			else
369
				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 307
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 307
  2. $upcontext is assigned
    in other/upgrade.php on line 308
  3. $upcontext is assigned
    in other/upgrade.php on line 309
  4. $upcontext is assigned
    in other/upgrade.php on line 322
  5. $upcontext is assigned
    in other/upgrade.php on line 358
  6. $upcontext is assigned
    in other/upgrade.php on line 359
  2. Path: Read from $_GET, and $v is assigned in other/upgrade.php on line 345
  1. Read from $_GET, and $v is assigned
    in other/upgrade.php on line 345
  2. $upcontext is assigned
    in other/upgrade.php on line 349
  3. $upcontext is assigned
    in other/upgrade.php on line 358
  4. $upcontext is assigned
    in other/upgrade.php on line 359
  3. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 359
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 359

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...
370
		}
371
372
		// Was there an error?
373
		if (!empty($upcontext['forced_error_message']))
374
			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 307
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 307
  2. $upcontext is assigned
    in other/upgrade.php on line 308
  3. $upcontext is assigned
    in other/upgrade.php on line 309
  4. $upcontext is assigned
    in other/upgrade.php on line 322
  2. Path: Read from $_GET, and $v is assigned in other/upgrade.php on line 345
  1. Read from $_GET, and $v is assigned
    in other/upgrade.php on line 345
  2. $upcontext is assigned
    in other/upgrade.php on line 349
  3. Path: Read from $_GET, and $upcontext is assigned in other/upgrade.php on line 359
  1. Read from $_GET, and $upcontext is assigned
    in other/upgrade.php on line 359

Preventing Cross-Site-Scripting Attacks

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

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

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

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

General Strategies to prevent injection

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

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

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

$sanitized = (integer) $tainted;
Loading history...
375
376
		// Show the footer.
377
		if (!isset($_GET['xml']))
378
			template_upgrade_below();
379
		else
380
			template_xml_below();
381
	}
382
383
384
	if (!empty($command_line) && $is_debug)
385
	{
386
		$active = time() - $upcontext['started'];
387
		$hours = floor($active / 3600);
388
		$minutes = intval(($active / 60) % 60);
389
		$seconds = intval($active % 60);
390
391
		$totalTime = '';
392
		if ($hours > 0)
393
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
394
		if ($minutes > 0)
395
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
396
		if ($seconds > 0)
397
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
398
399
		if (!empty($totalTime))
400
			echo "\n" . 'Upgrade completed in ' . $totalTime . "\n";
401
	}
402
403
	// Bang - gone!
404
	die();
405
}
406
407
// Used to direct the user to another location.
408
function redirectLocation($location, $addForm = true)
409
{
410
	global $upgradeurl, $upcontext, $command_line;
411
412
	// Command line users can't be redirected.
413
	if ($command_line)
414
		upgradeExit(true);
415
416
	// Are we providing the core info?
417
	if ($addForm)
418
	{
419
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
420
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
421
	}
422
423
	while (@ob_end_clean());
424
	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 420
  2. $location is passed through strtr()
    in other/upgrade.php on line 424

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...
425
426
	// Exit - saving status as we go.
427
	upgradeExit(true);
428
}
429
430
// Load all essential data and connect to the DB as this is pre SSI.php
431
function loadEssentialData()
432
{
433
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection, $db_prefix, $db_character_set, $db_type;
434
	global $modSettings, $sourcedir, $smcFunc;
435
436
	// Do the non-SSI stuff...
437
	if (function_exists('set_magic_quotes_runtime'))
438
		@set_magic_quotes_runtime(0);
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...
439
440
	error_reporting(E_ALL);
441
	define('SMF', 1);
442
443
	// Start the session.
444
	if (@ini_get('session.save_handler') == 'user')
445
		@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...
446
	@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...
447
448
	if (empty($smcFunc))
449
		$smcFunc = array();
450
451
	// We need this for authentication and some upgrade code
452
	require_once($sourcedir . '/Subs-Auth.php');
453
	require_once($sourcedir . '/Class-Package.php');
454
455
	$smcFunc['strtolower'] = 'smf_strtolower';
456
457
	// Initialize everything...
458
	initialize_inputs();
459
460
	// Get the database going!
461
	if (empty($db_type) || $db_type == 'mysqli')
462
		$db_type = 'mysql';
463
464
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
465
	{
466
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
467
468
		// Make the connection...
469
		if (empty($db_connection))
470
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, array('non_fatal' => true));
471
		else
472
			// If we've returned here, ping/reconnect to be safe
473
			$smcFunc['db_ping']($db_connection);
474
475
		// Oh dear god!!
476
		if ($db_connection === null)
477
			die('Unable to connect to database - please check username and password are correct in Settings.php');
478
479
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
480
			$smcFunc['db_query']('', '
481
			SET NAMES {string:db_character_set}',
482
			array(
483
				'db_error_skip' => true,
484
				'db_character_set' => $db_character_set,
485
			)
486
		);
487
488
		// Load the modSettings data...
489
		$request = $smcFunc['db_query']('', '
490
			SELECT variable, value
491
			FROM {db_prefix}settings',
492
			array(
493
				'db_error_skip' => true,
494
			)
495
		);
496
		$modSettings = array();
497 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
498
			$modSettings[$row['variable']] = $row['value'];
499
		$smcFunc['db_free_result']($request);
500
	}
501
	else
502
	{
503
		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.');
504
	}
505
506
	require_once($sourcedir . '/Subs.php');
507
508
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
509
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
510
	{
511
		require_once($sourcedir . '/QueryString.php');
512
		cleanRequest();
513
	}
514
515
	if (!isset($_GET['substep']))
516
		$_GET['substep'] = 0;
517
}
518
519
function initialize_inputs()
520
{
521
	global $start_time, $upcontext, $db_type;
522
523
	$start_time = time();
524
525
	umask(0);
526
527
	ob_start();
528
529
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
530
	ignore_user_abort(true);
531
532
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
533
	if (isset($_GET['delete']))
534
	{
535
		@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...
536
537
		// And the extra little files ;).
538
		@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...
539
		@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...
540
		@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...
541
		@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...
542
		@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...
543
544
		$dh = opendir(dirname(__FILE__));
545
		while ($file = readdir($dh))
546
		{
547
			if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
548
				@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...
549
		}
550
		closedir($dh);
551
552
		// Legacy files while we're at it. NOTE: We only touch files we KNOW shouldn't be there.
553
		// 1.1 Sources files not in 2.0+
554
		@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...
555
		// 1.1 Templates that don't exist any more (e.g. renamed)
556
		@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...
557
		@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...
558
		// 1.1 JS files were stored in the main theme folder, but in 2.0+ are in the scripts/ folder
559
		@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...
560
		@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...
561
		@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...
562
		@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...
563
		@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...
564
565
		// 2.0 Sources files not in 2.1+
566
		@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...
567
		@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...
568
569
		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 569

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...
570
		exit;
571
	}
572
573
	// Anybody home?
574 View Code Duplication
	if (!isset($_GET['xml']))
575
	{
576
		$upcontext['remote_files_available'] = false;
577
		$test = @fsockopen('www.simplemachines.org', 80, $errno, $errstr, 1);
578
		if ($test)
579
			$upcontext['remote_files_available'] = true;
580
		@fclose($test);
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...
581
	}
582
583
	// Something is causing this to happen, and it's annoying.  Stop it.
584
	$temp = 'upgrade_php?step';
585
	while (strlen($temp) > 4)
586
	{
587
		if (isset($_GET[$temp]))
588
			unset($_GET[$temp]);
589
		$temp = substr($temp, 1);
590
	}
591
592
	// Force a step, defaulting to 0.
593
	$_GET['step'] = (int) @$_GET['step'];
594
	$_GET['substep'] = (int) @$_GET['substep'];
595
}
596
597
// Step 0 - Let's welcome them in and ask them to login!
598
function WelcomeLogin()
599
{
600
	global $boarddir, $sourcedir, $modSettings, $cachedir, $upgradeurl, $upcontext;
601
	global $smcFunc, $db_type, $databases, $boardurl;
602
603
	// We global $txt here so that the language files can add to them. This variable is NOT unused.
604
	global $txt;
605
606
	$upcontext['sub_template'] = 'welcome_message';
607
608
	// Check for some key files - one template, one language, and a new and an old source file.
609
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
610
		&& @file_exists($sourcedir . '/QueryString.php')
611
		&& @file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')
612
		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
613
614
	// Need legacy scripts?
615 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.1)
616
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
617 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.0)
618
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-1.sql');
619 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 1.1)
620
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
621
622
	// We don't need "-utf8" files anymore...
623
	$upcontext['language'] = str_ireplace('-utf8', '', $upcontext['language']);
624
625
	// This needs to exist!
626
	if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
627
		return throw_error('The upgrader could not find the &quot;Install&quot; language file for the forum default language, ' . $upcontext['language'] . '.<br><br>Please make certain you uploaded all the files included in the package, even the theme and language files for the default theme.<br>&nbsp;&nbsp;&nbsp;[<a href="' . $upgradeurl . '?lang=english">Try English</a>]');
628
	else
629
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
630
631
	if (!$check)
632
		// Don't tell them what files exactly because it's a spot check - just like teachers don't tell which problems they are spot checking, that's dumb.
633
		return throw_error('The upgrader was unable to find some crucial files.<br><br>Please make sure you uploaded all of the files included in the package, including the Themes, Sources, and other directories.');
634
635
	// Do they meet the install requirements?
636
	if (!php_version_check())
637
		return throw_error('Warning!  You do not appear to have a version of PHP installed on your webserver that meets SMF\'s minimum installations requirements.<br><br>Please ask your host to upgrade.');
638
639
	if (!db_version_check())
640
		return throw_error('Your ' . $databases[$db_type]['name'] . ' version does not meet the minimum requirements of SMF.<br><br>Please ask your host to upgrade.');
641
642
	// Do some checks to make sure they have proper privileges
643
	db_extend('packages');
644
645
	// CREATE
646
	$create = $smcFunc['db_create_table']('{db_prefix}priv_check', array(array('name' => 'id_test', 'type' => 'int', 'size' => 10, 'unsigned' => true, 'auto' => true)), array(array('columns' => array('id_test'), 'type' => 'primary')), array(), 'overwrite');
647
648
	// ALTER
649
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
650
651
	// DROP
652
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
653
654
	// Sorry... we need CREATE, ALTER and DROP
655 View Code Duplication
	if (!$create || !$alter || !$drop)
656
		return throw_error('The ' . $databases[$db_type]['name'] . ' user you have set in Settings.php does not have proper privileges.<br><br>Please ask your host to give this user the ALTER, CREATE, and DROP privileges.');
657
658
	// Do a quick version spot check.
659
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
660
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
661
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
662
		return throw_error('The upgrader found some old or outdated files.<br><br>Please make certain you uploaded the new versions of all the files included in the package.');
663
664
	// What absolutely needs to be writable?
665
	$writable_files = array(
666
		$boarddir . '/Settings.php',
667
		$boarddir . '/Settings_bak.php',
668
		$boarddir . '/db_last_error.php',
669
		$modSettings['theme_dir'] . '/css/minified.css',
670
		$modSettings['theme_dir'] . '/scripts/minified.js',
671
		$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
672
	);
673
674
	// Do we need to add this setting?
675
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
676
677
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] . '/custom_avatar';
678
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
679
680
	// This little fellow has to cooperate...
681
	quickFileWritable($custom_av_dir);
682
683
	// Are we good now?
684
	if (!is_writable($custom_av_dir))
685
		return throw_error(sprintf('The directory: %1$s has to be writable to continue the upgrade. Please make sure permissions are correctly set to allow this.', $custom_av_dir));
686
	elseif ($need_settings_update)
687
	{
688
		if (!function_exists('cache_put_data'))
689
			require_once($sourcedir . '/Load.php');
690
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
691
		updateSettings(array('custom_avatar_url' => $custom_av_url));
692
	}
693
694
	require_once($sourcedir . '/Security.php');
695
696
	// Check the cache directory.
697
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
698
	if (!file_exists($cachedir_temp))
699
		@mkdir($cachedir_temp);
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...
700
	if (!file_exists($cachedir_temp))
701
		return throw_error('The cache directory could not be found.<br><br>Please make sure you have a directory called &quot;cache&quot; in your forum directory before continuing.');
702
703
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
704
		return throw_error('The upgrader was unable to find language files for the language specified in Settings.php.<br>SMF will not work without the primary language files installed.<br><br>Please either install them, or <a href="' . $upgradeurl . '?step=0;lang=english">use english instead</a>.');
705
	elseif (!isset($_GET['skiplang']))
706
	{
707
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
708
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
709
710
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
711
			return throw_error('The upgrader found some old or outdated language files, for the forum default language, ' . $upcontext['language'] . '.<br><br>Please make certain you uploaded the new versions of all the files included in the package, even the theme and language files for the default theme.<br>&nbsp;&nbsp;&nbsp;[<a href="' . $upgradeurl . '?skiplang">SKIP</a>] [<a href="' . $upgradeurl . '?lang=english">Try English</a>]');
712
	}
713
714
	if (!makeFilesWritable($writable_files))
715
		return false;
716
717
	// Check agreement.txt. (it may not exist, in which case $boarddir must be writable.)
718 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
719
		return throw_error('The upgrader was unable to obtain write access to agreement.txt.<br><br>If you are using a linux or unix based server, please ensure that the file is chmod\'d to 777, or if it does not exist that the directory this upgrader is in is 777.<br>If your server is running Windows, please ensure that the internet guest account has the proper permissions on it or its folder.');
720
721
	// Upgrade the agreement.
722
	elseif (isset($modSettings['agreement']))
723
	{
724
		$fp = fopen($boarddir . '/agreement.txt', 'w');
725
		fwrite($fp, $modSettings['agreement']);
726
		fclose($fp);
727
	}
728
729
	// We're going to check that their board dir setting is right in case they've been moving stuff around.
730
	if (strtr($boarddir, array('/' => '', '\\' => '')) != strtr(dirname(__FILE__), array('/' => '', '\\' => '')))
731
		$upcontext['warning'] = '
732
			It looks as if your board directory settings <em>might</em> be incorrect. Your board directory is currently set to &quot;' . $boarddir . '&quot; but should probably be &quot;' . dirname(__FILE__) . '&quot;. Settings.php currently lists your paths as:<br>
733
			<ul>
734
				<li>Board Directory: ' . $boarddir . '</li>
735
				<li>Source Directory: ' . $boarddir . '</li>
736
				<li>Cache Directory: ' . $cachedir_temp . '</li>
737
			</ul>
738
			If these seem incorrect please open Settings.php in a text editor before proceeding with this upgrade. If they are incorrect due to you moving your forum to a new location please download and execute the <a href="http://download.simplemachines.org/?tools">Repair Settings</a> tool from the Simple Machines website before continuing.';
739
740
	// Either we're logged in or we're going to present the login.
741
	if (checkLogin())
742
		return true;
743
744
	$upcontext += createToken('login');
745
746
	return false;
747
}
748
749
// Step 0.5: Does the login work?
750
function checkLogin()
751
{
752
	global $modSettings, $upcontext, $disable_security;
753
	global $smcFunc, $db_type, $support_js;
754
755
	// Don't bother if the security is disabled.
756
	if ($disable_security)
757
		return true;
758
759
	// Are we trying to login?
760
	if (isset($_POST['contbutt']) && (!empty($_POST['user'])))
761
	{
762
		// If we've disabled security pick a suitable name!
763
		if (empty($_POST['user']))
764
			$_POST['user'] = 'Administrator';
765
766
		// Before 2.0 these column names were different!
767
		$oldDB = false;
768
		if (empty($db_type) || $db_type == 'mysql')
769
		{
770
			$request = $smcFunc['db_query']('', '
771
				SHOW COLUMNS
772
				FROM {db_prefix}members
773
				LIKE {string:member_name}',
774
				array(
775
					'member_name' => 'memberName',
776
					'db_error_skip' => true,
777
				)
778
			);
779
			if ($smcFunc['db_num_rows']($request) != 0)
780
				$oldDB = true;
781
			$smcFunc['db_free_result']($request);
782
		}
783
784
		// Get what we believe to be their details.
785
		if (!$disable_security)
786
		{
787
			if ($oldDB)
788
				$request = $smcFunc['db_query']('', '
789
					SELECT id_member, memberName AS member_name, passwd, id_group,
790
					additionalGroups AS additional_groups, lngfile
791
					FROM {db_prefix}members
792
					WHERE memberName = {string:member_name}',
793
					array(
794
						'member_name' => $_POST['user'],
795
						'db_error_skip' => true,
796
					)
797
				);
798
			else
799
				$request = $smcFunc['db_query']('', '
800
					SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile
801
					FROM {db_prefix}members
802
					WHERE member_name = {string:member_name}',
803
					array(
804
						'member_name' => $_POST['user'],
805
						'db_error_skip' => true,
806
					)
807
				);
808
			if ($smcFunc['db_num_rows']($request) != 0)
809
			{
810
				list ($id_member, $name, $password, $id_group, $addGroups, $user_language) = $smcFunc['db_fetch_row']($request);
811
812
				$groups = explode(',', $addGroups);
813
				$groups[] = $id_group;
814
815
				foreach ($groups as $k => $v)
816
					$groups[$k] = (int) $v;
817
818
				$sha_passwd = sha1(strtolower($name) . un_htmlspecialchars($_REQUEST['passwrd']));
819
820
				// We don't use "-utf8" anymore...
821
				$user_language = str_ireplace('-utf8', '', $user_language);
822
			}
823
			else
824
				$upcontext['username_incorrect'] = true;
825
			$smcFunc['db_free_result']($request);
826
		}
827
		$upcontext['username'] = $_POST['user'];
828
829
		// Track whether javascript works!
830
		if (!empty($_POST['js_works']))
831
		{
832
			$upcontext['upgrade_status']['js'] = 1;
833
			$support_js = 1;
834
		}
835
		else
836
			$support_js = 0;
837
838
		// Note down the version we are coming from.
839
		if (!empty($modSettings['smfVersion']) && empty($upcontext['user']['version']))
840
			$upcontext['user']['version'] = $modSettings['smfVersion'];
841
842
		// Didn't get anywhere?
843
		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']))
844
		{
845
			// MD5?
846
			$md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user']));
847
			if ($md5pass != $password)
848
			{
849
				$upcontext['password_failed'] = true;
850
				// Disable the hashing this time.
851
				$upcontext['disable_login_hashing'] = true;
852
			}
853
		}
854
855
		if ((empty($upcontext['password_failed']) && !empty($name)) || $disable_security)
856
		{
857
			// Set the password.
858
			if (!$disable_security)
859
			{
860
				// Do we actually have permission?
861
				if (!in_array(1, $groups))
862
				{
863
					$request = $smcFunc['db_query']('', '
864
						SELECT permission
865
						FROM {db_prefix}permissions
866
						WHERE id_group IN ({array_int:groups})
867
							AND permission = {string:admin_forum}',
868
						array(
869
							'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...
870
							'admin_forum' => 'admin_forum',
871
							'db_error_skip' => true,
872
						)
873
					);
874
					if ($smcFunc['db_num_rows']($request) == 0)
875
						return throw_error('You need to be an admin to perform an upgrade!');
876
					$smcFunc['db_free_result']($request);
877
				}
878
879
				$upcontext['user']['id'] = $id_member;
880
				$upcontext['user']['name'] = $name;
881
			}
882
			else
883
			{
884
				$upcontext['user']['id'] = 1;
885
				$upcontext['user']['name'] = 'Administrator';
886
			}
887
			$upcontext['user']['pass'] = mt_rand(0, 60000);
888
			// This basically is used to match the GET variables to Settings.php.
889
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
890
891
			// Set the language to that of the user?
892
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
893
			{
894
				$user_language = basename($user_language, '.lng');
895
				$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
896
				preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
897
898
				if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
899
					$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'] . '.';
900
				elseif (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . basename($user_language, '.lng') . '.php'))
901
					$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'] . '.';
902
				else
903
				{
904
					// Set this as the new language.
905
					$upcontext['language'] = $user_language;
906
					$upcontext['upgrade_status']['lang'] = $upcontext['language'];
907
908
					// Include the file.
909
					require_once($modSettings['theme_dir'] . '/languages/Install.' . $user_language . '.php');
910
				}
911
			}
912
913
			// If we're resuming set the step and substep to be correct.
914
			if (isset($_POST['cont']))
915
			{
916
				$upcontext['current_step'] = $upcontext['user']['step'];
917
				$_GET['substep'] = $upcontext['user']['substep'];
918
			}
919
920
			return true;
921
		}
922
	}
923
924
	return false;
925
}
926
927
// Step 1: Do the maintenance and backup.
928
function UpgradeOptions()
929
{
930
	global $db_prefix, $command_line, $modSettings, $is_debug, $smcFunc, $packagesdir, $tasksdir, $language;
931
	global $boarddir, $boardurl, $sourcedir, $maintenance, $cachedir, $upcontext, $db_type, $db_server, $db_last_error;
932
933
	$upcontext['sub_template'] = 'upgrade_options';
934
	$upcontext['page_title'] = 'Upgrade Options';
935
936
	db_extend('packages');
937
	$upcontext['karma_installed'] = array('good' => false, 'bad' => false);
938
	$member_columns = $smcFunc['db_list_columns']('{db_prefix}members');
939
940
	$upcontext['karma_installed']['good'] = in_array('karma_good', $member_columns);
941
	$upcontext['karma_installed']['bad'] = in_array('karma_bad', $member_columns);
942
943
	unset($member_columns);
944
945
	// If we've not submitted then we're done.
946
	if (empty($_POST['upcont']))
947
		return false;
948
949
	// Firstly, if they're enabling SM stat collection just do it.
950
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']))
951
	{
952
		// Attempt to register the site etc.
953
		$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
954
		if ($fp)
955
		{
956
			$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
957
			$out .= 'Host: www.simplemachines.org' . "\r\n";
958
			$out .= 'Connection: Close' . "\r\n\r\n";
959
			fwrite($fp, $out);
960
961
			$return_data = '';
962
			while (!feof($fp))
963
				$return_data .= fgets($fp, 128);
964
965
			fclose($fp);
966
967
			// Get the unique site ID.
968
			preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);
969
970 View Code Duplication
			if (!empty($ID[1]))
971
				$smcFunc['db_insert']('replace',
972
					$db_prefix . 'settings',
973
					array('variable' => 'string', 'value' => 'string'),
974
					array('allow_sm_stats', $ID[1]),
975
					array('variable')
976
				);
977
		}
978
	}
979
	else
980
		$smcFunc['db_query']('', '
981
			DELETE FROM {db_prefix}settings
982
			WHERE variable = {string:allow_sm_stats}',
983
			array(
984
				'allow_sm_stats' => 'allow_sm_stats',
985
				'db_error_skip' => true,
986
			)
987
		);
988
989
	// Deleting old karma stuff?
990
	if (!empty($_POST['delete_karma']))
991
	{
992
		// Delete old settings vars.
993
		$smcFunc['db_query']('', '
994
			DELETE FROM {db_prefix}settings
995
			WHERE variable IN ({array_string:karma_vars})',
996
			array(
997
				'karma_vars' => array('karmaMode', 'karmaTimeRestrictAdmins', 'karmaWaitTime', 'karmaMinPosts', 'karmaLabel', 'karmaSmiteLabel', 'karmaApplaudLabel'),
998
			)
999
		);
1000
1001
		// Cleaning up old karma member settings.
1002
		if ($upcontext['karma_installed']['good'])
1003
			$smcFunc['db_query']('', '
1004
				ALTER TABLE {db_prefix}members
1005
				DROP karma_good',
1006
				array()
1007
			);
1008
1009
		// Does karma bad was enable?
1010
		if ($upcontext['karma_installed']['bad'])
1011
			$smcFunc['db_query']('', '
1012
				ALTER TABLE {db_prefix}members
1013
				DROP karma_bad',
1014
				array()
1015
			);
1016
1017
		// Cleaning up old karma permissions.
1018
		$smcFunc['db_query']('', '
1019
			DELETE FROM {db_prefix}permissions
1020
			WHERE permission = {string:karma_vars}',
1021
			array(
1022
				'karma_vars' => 'karma_edit',
1023
			)
1024
		);
1025
	}
1026
1027
	// Emptying the error log?
1028
	if (!empty($_POST['empty_error']))
1029
		$smcFunc['db_query']('truncate_table', '
1030
			TRUNCATE {db_prefix}log_errors',
1031
			array(
1032
			)
1033
		);
1034
1035
	$changes = array();
1036
1037
	// Add proxy settings.
1038
	if (!isset($GLOBALS['image_proxy_maxsize']))
1039
		$changes += array(
1040
			'image_proxy_secret' => '\'' . substr(sha1(mt_rand()), 0, 20) . '\'',
1041
			'image_proxy_maxsize' => 5190,
1042
			'image_proxy_enabled' => 0,
1043
		);
1044
1045
	// If we're overriding the language follow it through.
1046
	if (isset($_GET['lang']) && file_exists($modSettings['theme_dir'] . '/languages/index.' . $_GET['lang'] . '.php'))
1047
		$changes['language'] = '\'' . $_GET['lang'] . '\'';
1048
1049
	if (!empty($_POST['maint']))
1050
	{
1051
		$changes['maintenance'] = '2';
1052
		// Remember what it was...
1053
		$upcontext['user']['main'] = $maintenance;
1054
1055
		if (!empty($_POST['maintitle']))
1056
		{
1057
			$changes['mtitle'] = '\'' . addslashes($_POST['maintitle']) . '\'';
1058
			$changes['mmessage'] = '\'' . addslashes($_POST['mainmessage']) . '\'';
1059
		}
1060
		else
1061
		{
1062
			$changes['mtitle'] = '\'Upgrading the forum...\'';
1063
			$changes['mmessage'] = '\'Don\\\'t worry, we will be back shortly with an updated forum.  It will only be a minute ;).\'';
1064
		}
1065
	}
1066
1067
	if ($command_line)
1068
		echo ' * Updating Settings.php...';
1069
1070
	// Fix some old paths.
1071
	if (substr($boarddir, 0, 1) == '.')
1072
		$changes['boarddir'] = '\'' . fixRelativePath($boarddir) . '\'';
1073
1074
	if (substr($sourcedir, 0, 1) == '.')
1075
		$changes['sourcedir'] = '\'' . fixRelativePath($sourcedir) . '\'';
1076
1077
	if (empty($cachedir) || substr($cachedir, 0, 1) == '.')
1078
		$changes['cachedir'] = '\'' . fixRelativePath($boarddir) . '/cache\'';
1079
1080
	// Not had the database type added before?
1081
	if (empty($db_type))
1082
		$changes['db_type'] = 'mysql';
1083
1084
	// If they have a "host:port" setup for the host, split that into separate values
1085
	// You should never have a : in the hostname if you're not on MySQL, but better safe than sorry
1086
	if (strpos($db_server, ':') !== false && $db_type == 'mysql')
1087
	{
1088
		list ($db_server, $db_port) = explode(':', $db_server);
1089
1090
		$changes['db_server'] = '\'' . $db_server . '\'';
1091
1092
		// Only set this if we're not using the default port
1093
		if ($db_port != ini_get('mysqli.default_port'))
1094
			$changes['db_port'] = (int) $db_port;
1095
	}
1096
	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...
1097
	{
1098
		// If db_port is set and is the same as the default, set it to ''
1099
		if ($db_type == 'mysql')
1100
		{
1101
			if ($db_port == ini_get('mysqli.default_port'))
1102
				$changes['db_port'] = '\'\'';
1103
			elseif ($db_type == 'postgresql' && $db_port == 5432)
1104
				$changes['db_port'] = '\'\'';
1105
		}
1106
	}
1107
1108
	// Maybe we haven't had this option yet?
1109
	if (empty($packagesdir))
1110
		$changes['packagesdir'] = '\'' . fixRelativePath($boarddir) . '/Packages\'';
1111
1112
	// Add support for $tasksdir var.
1113
	if (empty($tasksdir))
1114
		$changes['tasksdir'] = '\'' . fixRelativePath($sourcedir) . '/tasks\'';
1115
1116
	// Make sure we fix the language as well.
1117
	if (stristr($language, '-utf8'))
1118
		$changes['language'] = '\'' . str_ireplace('-utf8', '', $language) . '\'';
1119
1120
	// @todo Maybe change the cookie name if going to 1.1, too?
1121
1122
	// Update Settings.php with the new settings.
1123
	require_once($sourcedir . '/Subs-Admin.php');
1124
	updateSettingsFile($changes);
1125
1126
	if ($command_line)
1127
		echo ' Successful.' . "\n";
1128
1129
	// Are we doing debug?
1130
	if (isset($_POST['debug']))
1131
	{
1132
		$upcontext['upgrade_status']['debug'] = true;
1133
		$is_debug = true;
1134
	}
1135
1136
	// If we're not backing up then jump one.
1137
	if (empty($_POST['backup']))
1138
		$upcontext['current_step']++;
1139
1140
	// If we've got here then let's proceed to the next step!
1141
	return true;
1142
}
1143
1144
// Backup the database - why not...
1145
function BackupDatabase()
1146
{
1147
	global $upcontext, $db_prefix, $command_line, $support_js, $file_steps, $smcFunc;
1148
1149
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1150
	$upcontext['page_title'] = 'Backup Database';
1151
1152
	// Done it already - js wise?
1153
	if (!empty($_POST['backup_done']))
1154
		return true;
1155
1156
	// Some useful stuff here.
1157
	db_extend();
1158
1159
	// Might need this as well
1160
	db_extend('packages');
1161
1162
	// Get all the table names.
1163
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1164
	$db = preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? strtr($match[1], array('`' => '')) : false;
1165
	$tables = $smcFunc['db_list_tables']($db, $filter);
1166
1167
	$table_names = array();
1168
	foreach ($tables as $table)
1169
		if (substr($table, 0, 7) !== 'backup_')
1170
			$table_names[] = $table;
1171
1172
	$upcontext['table_count'] = count($table_names);
1173
	$upcontext['cur_table_num'] = $_GET['substep'];
1174
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1175
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1176
	// For non-java auto submit...
1177
	$file_steps = $upcontext['table_count'];
1178
1179
	// What ones have we already done?
1180 View Code Duplication
	foreach ($table_names as $id => $table)
1181
		if ($id < $_GET['substep'])
1182
			$upcontext['previous_tables'][] = $table;
1183
1184
	if ($command_line)
1185
		echo 'Backing Up Tables.';
1186
1187
	// If we don't support javascript we backup here.
1188
	if (!$support_js || isset($_GET['xml']))
1189
	{
1190
		// Backup each table!
1191
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1192
		{
1193
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1194
			$upcontext['cur_table_num'] = $substep + 1;
1195
1196
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1197
1198
			// Do we need to pause?
1199
			nextSubstep($substep);
1200
1201
			backupTable($table_names[$substep]);
1202
1203
			// If this is XML to keep it nice for the user do one table at a time anyway!
1204
			if (isset($_GET['xml']))
1205
				return upgradeExit();
1206
		}
1207
1208
		if ($command_line)
1209
		{
1210
			echo "\n" . ' Successful.\'' . "\n";
1211
			flush();
1212
		}
1213
		$upcontext['step_progress'] = 100;
1214
1215
		$_GET['substep'] = 0;
1216
		// Make sure we move on!
1217
		return true;
1218
	}
1219
1220
	// Either way next place to post will be database changes!
1221
	$_GET['substep'] = 0;
1222
	return false;
1223
}
1224
1225
// Backup one table...
1226
function backupTable($table)
1227
{
1228
	global $command_line, $db_prefix, $smcFunc;
1229
1230
	if ($command_line)
1231
	{
1232
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1233
		flush();
1234
	}
1235
1236
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1237
1238
	if ($command_line)
1239
		echo ' done.';
1240
}
1241
1242
// Step 2: Everything.
1243
function DatabaseChanges()
1244
{
1245
	global $db_prefix, $modSettings, $smcFunc;
1246
	global $upcontext, $support_js, $db_type;
1247
1248
	// Have we just completed this?
1249
	if (!empty($_POST['database_done']))
1250
		return true;
1251
1252
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1253
	$upcontext['page_title'] = 'Database Changes';
1254
1255
	// All possible files.
1256
	// Name, < version, insert_on_complete
1257
	$files = array(
1258
		array('upgrade_1-0.sql', '1.1', '1.1 RC0'),
1259
		array('upgrade_1-1.sql', '2.0', '2.0 a'),
1260
		array('upgrade_2-0_' . $db_type . '.sql', '2.1', '2.1 dev0'),
1261
		array('upgrade_2-1_' . $db_type . '.sql', '3.0', SMF_VERSION),
1262
	);
1263
1264
	// How many files are there in total?
1265
	if (isset($_GET['filecount']))
1266
		$upcontext['file_count'] = (int) $_GET['filecount'];
1267
	else
1268
	{
1269
		$upcontext['file_count'] = 0;
1270
		foreach ($files as $file)
1271
		{
1272
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1273
				$upcontext['file_count']++;
1274
		}
1275
	}
1276
1277
	// Do each file!
1278
	$did_not_do = count($files) - $upcontext['file_count'];
1279
	$upcontext['step_progress'] = 0;
1280
	$upcontext['cur_file_num'] = 0;
1281
	foreach ($files as $file)
1282
	{
1283
		if ($did_not_do)
1284
			$did_not_do--;
1285
		else
1286
		{
1287
			$upcontext['cur_file_num']++;
1288
			$upcontext['cur_file_name'] = $file[0];
1289
			// Do we actually need to do this still?
1290
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1291
			{
1292
				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1293 View Code Duplication
				if ($nextFile)
1294
				{
1295
					// Only update the version of this if complete.
1296
					$smcFunc['db_insert']('replace',
1297
						$db_prefix . 'settings',
1298
						array('variable' => 'string', 'value' => 'string'),
1299
						array('smfVersion', $file[2]),
1300
						array('variable')
1301
					);
1302
1303
					$modSettings['smfVersion'] = $file[2];
1304
				}
1305
1306
				// If this is XML we only do this stuff once.
1307
				if (isset($_GET['xml']))
1308
				{
1309
					// Flag to move on to the next.
1310
					$upcontext['completed_step'] = true;
1311
					// Did we complete the whole file?
1312
					if ($nextFile)
1313
						$upcontext['current_debug_item_num'] = -1;
1314
					return upgradeExit();
1315
				}
1316
				elseif ($support_js)
1317
					break;
1318
			}
1319
			// Set the progress bar to be right as if we had - even if we hadn't...
1320
			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1321
		}
1322
	}
1323
1324
	$_GET['substep'] = 0;
1325
	// So the template knows we're done.
1326
	if (!$support_js)
1327
	{
1328
		$upcontext['changes_complete'] = true;
1329
1330
		return true;
1331
	}
1332
	return false;
1333
}
1334
1335
1336
// Delete the damn thing!
1337
function DeleteUpgrade()
1338
{
1339
	global $command_line, $language, $upcontext, $boarddir, $sourcedir, $forum_version, $user_info, $maintenance, $smcFunc, $db_type;
1340
1341
	// Now it's nice to have some of the basic SMF source files.
1342
	if (!isset($_GET['ssi']) && !$command_line)
1343
		redirectLocation('&ssi=1');
1344
1345
	$upcontext['sub_template'] = 'upgrade_complete';
1346
	$upcontext['page_title'] = 'Upgrade Complete';
1347
1348
	$endl = $command_line ? "\n" : '<br>' . "\n";
1349
1350
	$changes = array(
1351
		'language' => '\'' . (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language) . '\'',
1352
		'db_error_send' => '1',
1353
		'upgradeData' => '\'\'',
1354
	);
1355
1356
	// Are we in maintenance mode?
1357
	if (isset($upcontext['user']['main']))
1358
	{
1359
		if ($command_line)
1360
			echo ' * ';
1361
		$upcontext['removed_maintenance'] = true;
1362
		$changes['maintenance'] = $upcontext['user']['main'];
1363
	}
1364
	// Otherwise if somehow we are in 2 let's go to 1.
1365
	elseif (!empty($maintenance) && $maintenance == 2)
1366
		$changes['maintenance'] = 1;
1367
1368
	// Wipe this out...
1369
	$upcontext['user'] = array();
1370
1371
	require_once($sourcedir . '/Subs-Admin.php');
1372
	updateSettingsFile($changes);
1373
1374
	// Clean any old cache files away.
1375
	upgrade_clean_cache();
1376
1377
	// Can we delete the file?
1378
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1379
1380
	// Now is the perfect time to fetch the SM files.
1381
	if ($command_line)
1382
		cli_scheduled_fetchSMfiles();
1383
	else
1384
	{
1385
		require_once($sourcedir . '/ScheduledTasks.php');
1386
		$forum_version = SMF_VERSION; // The variable is usually defined in index.php so lets just use the constant to do it for us.
1387
		scheduled_fetchSMfiles(); // Now go get those files!
1388
	}
1389
1390
	// Log what we've done.
1391
	if (empty($user_info['id']))
1392
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
1393
1394
	// Log the action manually, so CLI still works.
1395
	$smcFunc['db_insert']('',
1396
		'{db_prefix}log_actions',
1397
		array(
1398
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'inet', 'action' => 'string',
1399
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
1400
		),
1401
		array(
1402
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
1403
			0, 0, 0, json_encode(array('version' => $forum_version, 'member' => $user_info['id'])),
1404
		),
1405
		array('id_action')
1406
	);
1407
	$user_info['id'] = 0;
1408
1409
	// Save the current database version.
1410
	$server_version = $smcFunc['db_server_info']();
1411 View Code Duplication
	if ($db_type == 'mysql' && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51')))
1412
		updateSettings(array('db_mysql_group_by_fix' => '1'));
1413
1414
	if ($command_line)
1415
	{
1416
		echo $endl;
1417
		echo 'Upgrade Complete!', $endl;
1418
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
1419
		exit;
1420
	}
1421
1422
	// Make sure it says we're done.
1423
	$upcontext['overall_percent'] = 100;
1424
	if (isset($upcontext['step_progress']))
1425
		unset($upcontext['step_progress']);
1426
1427
	$_GET['substep'] = 0;
1428
	return false;
1429
}
1430
1431
// Just like the built in one, but setup for CLI to not use themes.
1432
function cli_scheduled_fetchSMfiles()
1433
{
1434
	global $sourcedir, $language, $forum_version, $modSettings, $smcFunc;
1435
1436
	if (empty($modSettings['time_format']))
1437
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1438
1439
	// What files do we want to get
1440
	$request = $smcFunc['db_query']('', '
1441
		SELECT id_file, filename, path, parameters
1442
		FROM {db_prefix}admin_info_files',
1443
		array(
1444
		)
1445
	);
1446
1447
	$js_files = array();
1448 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
1449
	{
1450
		$js_files[$row['id_file']] = array(
1451
			'filename' => $row['filename'],
1452
			'path' => $row['path'],
1453
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode($forum_version)),
1454
		);
1455
	}
1456
	$smcFunc['db_free_result']($request);
1457
1458
	// We're gonna need fetch_web_data() to pull this off.
1459
	require_once($sourcedir . '/Subs-Package.php');
1460
1461
	foreach ($js_files as $ID_FILE => $file)
1462
	{
1463
		// Create the url
1464
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'http://www.simplemachines.org' : '';
1465
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1466
1467
		// Get the file
1468
		$file_data = fetch_web_data($url);
1469
1470
		// If we got an error - give up - the site might be down.
1471
		if ($file_data === false)
1472
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
1473
1474
		// Save the file to the database.
1475
		$smcFunc['db_query']('substring', '
1476
			UPDATE {db_prefix}admin_info_files
1477
			SET data = SUBSTRING({string:file_data}, 1, 65534)
1478
			WHERE id_file = {int:id_file}',
1479
			array(
1480
				'id_file' => $ID_FILE,
1481
				'file_data' => $file_data,
1482
			)
1483
		);
1484
	}
1485
	return true;
1486
}
1487
1488
function convertSettingsToTheme()
1489
{
1490
	global $db_prefix, $modSettings, $smcFunc;
1491
1492
	$values = array(
1493
		'show_latest_member' => @$GLOBALS['showlatestmember'],
1494
		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
1495
		'show_modify' => @$GLOBALS['showmodify'],
1496
		'show_user_images' => @$GLOBALS['showuserpic'],
1497
		'show_blurb' => @$GLOBALS['showusertext'],
1498
		'show_gender' => @$GLOBALS['showgenderimage'],
1499
		'show_newsfader' => @$GLOBALS['shownewsfader'],
1500
		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
1501
		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
1502
		'linktree_link' => @$GLOBALS['curposlinks'],
1503
		'show_profile_buttons' => @$GLOBALS['profilebutton'],
1504
		'show_mark_read' => @$GLOBALS['showmarkread'],
1505
		'newsfader_time' => @$GLOBALS['fadertime'],
1506
		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
1507
		'enable_news' => @$GLOBALS['enable_news'],
1508
		'return_to_post' => @$modSettings['returnToPost'],
1509
	);
1510
1511
	$themeData = array();
1512
	foreach ($values as $variable => $value)
1513
	{
1514
		if (!isset($value) || $value === null)
1515
			$value = 0;
1516
1517
		$themeData[] = array(0, 1, $variable, $value);
1518
	}
1519 View Code Duplication
	if (!empty($themeData))
1520
	{
1521
		$smcFunc['db_insert']('ignore',
1522
			$db_prefix . 'themes',
1523
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
1524
			$themeData,
1525
			array('id_member', 'id_theme', 'variable')
1526
		);
1527
	}
1528
}
1529
1530
// This function only works with MySQL but that's fine as it is only used for v1.0.
1531
function convertSettingstoOptions()
1532
{
1533
	global $modSettings, $smcFunc;
1534
1535
	// Format: new_setting -> old_setting_name.
1536
	$values = array(
1537
		'calendar_start_day' => 'cal_startmonday',
1538
		'view_newest_first' => 'viewNewestFirst',
1539
		'view_newest_pm_first' => 'viewNewestFirst',
1540
	);
1541
1542
	foreach ($values as $variable => $value)
1543
	{
1544
		if (empty($modSettings[$value[0]]))
1545
			continue;
1546
1547
		$smcFunc['db_query']('', '
1548
			INSERT IGNORE INTO {db_prefix}themes
1549
				(id_member, id_theme, variable, value)
1550
			SELECT id_member, 1, {string:variable}, {string:value}
1551
			FROM {db_prefix}members',
1552
			array(
1553
				'variable' => $variable,
1554
				'value' => $modSettings[$value[0]],
1555
				'db_error_skip' => true,
1556
			)
1557
		);
1558
1559
		$smcFunc['db_query']('', '
1560
			INSERT IGNORE INTO {db_prefix}themes
1561
				(id_member, id_theme, variable, value)
1562
			VALUES (-1, 1, {string:variable}, {string:value})',
1563
			array(
1564
				'variable' => $variable,
1565
				'value' => $modSettings[$value[0]],
1566
				'db_error_skip' => true,
1567
			)
1568
		);
1569
	}
1570
}
1571
1572
function php_version_check()
1573
{
1574
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
1575
}
1576
1577
function db_version_check()
1578
{
1579
	global $db_type, $databases;
1580
1581
	$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...
1582
	$curver = preg_replace('~\-.+?$~', '', $curver);
1583
1584
	return version_compare($databases[$db_type]['version'], $curver, '<=');
1585
}
1586
1587
function fixRelativePath($path)
1588
{
1589
	global $install_path;
1590
1591
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
1592
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
1593
}
1594
1595
function parse_sql($filename)
1596
{
1597
	global $db_prefix, $db_collation, $boarddir, $boardurl, $command_line, $file_steps, $step_progress, $custom_warning;
1598
	global $upcontext, $support_js, $is_debug, $smcFunc, $databases, $db_type, $db_character_set;
1599
1600
/*
1601
	Failure allowed on:
1602
		- INSERT INTO but not INSERT IGNORE INTO.
1603
		- UPDATE IGNORE but not UPDATE.
1604
		- ALTER TABLE and ALTER IGNORE TABLE.
1605
		- DROP TABLE.
1606
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
1607
1608
	If a comment...
1609
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
1610
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
1611
		- is only ---#, it is "done." and then a break - only shown in debug.
1612
		- begins with ---{ it is a code block terminating at ---}.
1613
1614
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
1615
1616
	Replaces the following variables:
1617
		- {$boarddir}
1618
		- {$boardurl}
1619
		- {$db_prefix}
1620
		- {$db_collation}
1621
*/
1622
1623
	// May want to use extended functionality.
1624
	db_extend();
1625
	db_extend('packages');
1626
1627
	// Our custom error handler - does nothing but does stop public errors from XML!
1628
	set_error_handler(
1629
		function ($errno, $errstr, $errfile, $errline) use ($support_js)
1630
		{
1631
			if ($support_js)
1632
				return true;
1633
			else
1634
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
1635
		}
1636
	);
1637
1638
	// If we're on MySQL supporting collations then let's find out what the members table uses and put it in a global var - to allow upgrade script to match collations!
1639
	if (!empty($databases[$db_type]['utf8_support']) && version_compare($databases[$db_type]['utf8_version'], eval($databases[$db_type]['utf8_version_check']), '>'))
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...
1640
	{
1641
		$request = $smcFunc['db_query']('', '
1642
			SHOW TABLE STATUS
1643
			LIKE {string:table_name}',
1644
			array(
1645
				'table_name' => "{$db_prefix}members",
1646
				'db_error_skip' => true,
1647
			)
1648
		);
1649
		if ($smcFunc['db_num_rows']($request) === 0)
1650
			die('Unable to find members table!');
1651
		$table_status = $smcFunc['db_fetch_assoc']($request);
1652
		$smcFunc['db_free_result']($request);
1653
1654
		if (!empty($table_status['Collation']))
1655
		{
1656
			$request = $smcFunc['db_query']('', '
1657
				SHOW COLLATION
1658
				LIKE {string:collation}',
1659
				array(
1660
					'collation' => $table_status['Collation'],
1661
					'db_error_skip' => true,
1662
				)
1663
			);
1664
			// Got something?
1665
			if ($smcFunc['db_num_rows']($request) !== 0)
1666
				$collation_info = $smcFunc['db_fetch_assoc']($request);
1667
			$smcFunc['db_free_result']($request);
1668
1669
			// Excellent!
1670
			if (!empty($collation_info['Collation']) && !empty($collation_info['Charset']))
1671
				$db_collation = ' CHARACTER SET ' . $collation_info['Charset'] . ' COLLATE ' . $collation_info['Collation'];
1672
		}
1673
	}
1674
	if (empty($db_collation))
1675
		$db_collation = '';
1676
1677
	$endl = $command_line ? "\n" : '<br>' . "\n";
1678
1679
	$lines = file($filename);
1680
1681
	$current_type = 'sql';
1682
	$current_data = '';
1683
	$substep = 0;
1684
	$last_step = '';
1685
1686
	// Make sure all newly created tables will have the proper characters set.
1687
	if (isset($db_character_set) && $db_character_set === 'utf8')
1688
		$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
1689
1690
	// Count the total number of steps within this file - for progress.
1691
	$file_steps = substr_count(implode('', $lines), '---#');
1692
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
1693
	$upcontext['debug_items'] = $file_steps;
1694
	$upcontext['current_item_num'] = 0;
1695
	$upcontext['current_item_name'] = '';
1696
	$upcontext['current_debug_item_num'] = 0;
1697
	$upcontext['current_debug_item_name'] = '';
1698
	// This array keeps a record of what we've done in case java is dead...
1699
	$upcontext['actioned_items'] = array();
1700
1701
	$done_something = false;
1702
1703
	foreach ($lines as $line_number => $line)
1704
	{
1705
		$do_current = $substep >= $_GET['substep'];
1706
1707
		// Get rid of any comments in the beginning of the line...
1708
		if (substr(trim($line), 0, 2) === '/*')
1709
			$line = preg_replace('~/\*.+?\*/~', '', $line);
1710
1711
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
1712
		if ($is_debug && !$support_js && $command_line)
1713
			flush();
1714
1715
		if (trim($line) === '')
1716
			continue;
1717
1718
		if (trim(substr($line, 0, 3)) === '---')
1719
		{
1720
			$type = substr($line, 3, 1);
1721
1722
			// An error??
1723
			if (trim($current_data) != '' && $type !== '}')
1724
			{
1725
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
1726
				if ($command_line)
1727
					echo $upcontext['error_message'];
1728
			}
1729
1730
			if ($type == ' ')
1731
			{
1732
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
1733
				{
1734
					echo ' Successful.', $endl;
1735
					flush();
1736
				}
1737
1738
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
1739
				$upcontext['current_item_num']++;
1740
				$upcontext['current_item_name'] = $last_step;
1741
1742
				if ($do_current)
1743
				{
1744
					$upcontext['actioned_items'][] = $last_step;
1745
					if ($command_line)
1746
						echo ' * ';
1747
				}
1748
			}
1749
			elseif ($type == '#')
1750
			{
1751
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
1752
1753
				$upcontext['current_debug_item_num']++;
1754
				if (trim($line) != '---#')
1755
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
1756
1757
				// Have we already done something?
1758
				if (isset($_GET['xml']) && $done_something)
1759
				{
1760
					restore_error_handler();
1761
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
1762
				}
1763
1764
				if ($do_current)
1765
				{
1766
					if (trim($line) == '---#' && $command_line)
1767
						echo ' done.', $endl;
1768
					elseif ($command_line)
1769
						echo ' +++ ', rtrim(substr($line, 4));
1770
					elseif (trim($line) != '---#')
1771
					{
1772
						if ($is_debug)
1773
							$upcontext['actioned_items'][] = htmlspecialchars(rtrim(substr($line, 4)));
1774
					}
1775
				}
1776
1777
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
1778
				{
1779
					if ($command_line)
1780
						echo ' * ';
1781
					else
1782
						$upcontext['actioned_items'][] = $last_step;
1783
				}
1784
1785
				// Small step - only if we're actually doing stuff.
1786
				if ($do_current)
1787
					nextSubstep(++$substep);
1788
				else
1789
					$substep++;
1790
			}
1791
			elseif ($type == '{')
1792
				$current_type = 'code';
1793
			elseif ($type == '}')
1794
			{
1795
				$current_type = 'sql';
1796
1797
				if (!$do_current)
1798
				{
1799
					$current_data = '';
1800
					continue;
1801
				}
1802
1803
				if (eval('global $db_prefix, $modSettings, $smcFunc; ' . $current_data) === false)
0 ignored issues
show
Coding Style introduced by
The function parse_sql() contains an eval expression.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
1804
				{
1805
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
1806
					if ($command_line)
1807
						echo $upcontext['error_message'];
1808
				}
1809
1810
				// Done with code!
1811
				$current_data = '';
1812
				$done_something = true;
1813
			}
1814
1815
			continue;
1816
		}
1817
1818
		$current_data .= $line;
1819
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
1820
		{
1821
			if ((!$support_js || isset($_GET['xml'])))
1822
			{
1823
				if (!$do_current)
1824
				{
1825
					$current_data = '';
1826
					continue;
1827
				}
1828
1829
				$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));
1830
1831
				upgrade_query($current_data);
1832
1833
				// @todo This will be how it kinda does it once mysql all stripped out - needed for postgre (etc).
1834
				/*
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...
1835
				$result = $smcFunc['db_query']('', $current_data, false, false);
1836
				// Went wrong?
1837
				if (!$result)
1838
				{
1839
					// Bit of a bodge - do we want the error?
1840
					if (!empty($upcontext['return_error']))
1841
					{
1842
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
1843
						return false;
1844
					}
1845
				}*/
1846
				$done_something = true;
1847
			}
1848
			$current_data = '';
1849
		}
1850
		// If this is xml based and we're just getting the item name then that's grand.
1851
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
1852
		{
1853
			restore_error_handler();
1854
			return false;
1855
		}
1856
1857
		// Clean up by cleaning any step info.
1858
		$step_progress = array();
1859
		$custom_warning = '';
1860
	}
1861
1862
	// Put back the error handler.
1863
	restore_error_handler();
1864
1865
	if ($command_line)
1866
	{
1867
		echo ' Successful.' . "\n";
1868
		flush();
1869
	}
1870
1871
	$_GET['substep'] = 0;
1872
	return true;
1873
}
1874
1875
function upgrade_query($string, $unbuffered = false)
1876
{
1877
	global $db_connection, $db_server, $db_user, $db_passwd, $db_type, $command_line, $upcontext, $upgradeurl, $modSettings;
1878
	global $db_name, $db_unbuffered, $smcFunc;
1879
1880
	// Get the query result - working around some SMF specific security - just this once!
1881
	$modSettings['disableQueryCheck'] = true;
1882
	$db_unbuffered = $unbuffered;
1883
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
1884
	$db_unbuffered = false;
1885
1886
	// Failure?!
1887
	if ($result !== false)
1888
		return $result;
1889
1890
	$db_error_message = $smcFunc['db_error']($db_connection);
1891
	// If MySQL we do something more clever.
1892
	if ($db_type == 'mysql')
1893
	{
1894
		$mysqli_errno = mysqli_errno($db_connection);
1895
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR'));
1896
1897
		// Error numbers:
1898
		//    1016: Can't open file '....MYI'
1899
		//    1050: Table already exists.
1900
		//    1054: Unknown column name.
1901
		//    1060: Duplicate column name.
1902
		//    1061: Duplicate key name.
1903
		//    1062: Duplicate entry for unique key.
1904
		//    1068: Multiple primary keys.
1905
		//    1072: Key column '%s' doesn't exist in table.
1906
		//    1091: Can't drop key, doesn't exist.
1907
		//    1146: Table doesn't exist.
1908
		//    2013: Lost connection to server during query.
1909
1910
		if ($mysqli_errno == 1016)
1911
		{
1912
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
1913
			{
1914
				mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
1915
				$result = mysqli_query($db_connection, $string);
1916
				if ($result !== false)
1917
					return $result;
1918
			}
1919
		}
1920
		elseif ($mysqli_errno == 2013)
1921
		{
1922
			$db_connection = mysqli_connect($db_server, $db_user, $db_passwd);
1923
			mysqli_select_db($db_connection, $db_name);
1924
			if ($db_connection)
1925
			{
1926
				$result = mysqli_query($db_connection, $string);
1927
				if ($result !== false)
1928
					return $result;
1929
			}
1930
		}
1931
		// Duplicate column name... should be okay ;).
1932 View Code Duplication
		elseif (in_array($mysqli_errno, array(1060, 1061, 1068, 1091)))
1933
			return false;
1934
		// Duplicate insert... make sure it's the proper type of query ;).
1935 View Code Duplication
		elseif (in_array($mysqli_errno, array(1054, 1062, 1146)) && $error_query)
1936
			return false;
1937
		// Creating an index on a non-existent column.
1938
		elseif ($mysqli_errno == 1072)
1939
			return false;
1940
		elseif ($mysqli_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
1941
			return false;
1942
	}
1943
	// If a table already exists don't go potty.
1944
	else
1945
	{
1946
		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U')))
1947
		{
1948
			if (strpos($db_error_message, 'exist') !== false)
1949
				return true;
1950
		}
1951
		elseif (strpos(trim($string), 'INSERT ') !== false)
1952
		{
1953
			if (strpos($db_error_message, 'duplicate') !== false)
1954
				return true;
1955
		}
1956
	}
1957
1958
	// Get the query string so we pass everything.
1959
	$query_string = '';
1960
	foreach ($_GET as $k => $v)
1961
		$query_string .= ';' . $k . '=' . $v;
1962
	if (strlen($query_string) != 0)
1963
		$query_string = '?' . substr($query_string, 1);
1964
1965
	if ($command_line)
1966
	{
1967
		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
1968
		die;
1969
	}
1970
1971
	// Bit of a bodge - do we want the error?
1972
	if (!empty($upcontext['return_error']))
1973
	{
1974
		$upcontext['error_message'] = $db_error_message;
1975
		$upcontext['error_string'] = $string;
1976
		return false;
1977
	}
1978
1979
	// Otherwise we have to display this somewhere appropriate if possible.
1980
	$upcontext['forced_error_message'] = '
1981
			<strong>Unsuccessful!</strong><br>
1982
1983
			<div style="margin: 2ex;">
1984
				This query:
1985
				<blockquote><pre>' . nl2br(htmlspecialchars(trim($string))) . ';</pre></blockquote>
1986
1987
				Caused the error:
1988
				<blockquote>' . nl2br(htmlspecialchars($db_error_message)) . '</blockquote>
1989
			</div>
1990
1991
			<form action="' . $upgradeurl . $query_string . '" method="post">
1992
				<input type="submit" value="Try again" class="button_submit">
1993
			</form>
1994
		</div>';
1995
1996
	upgradeExit();
1997
}
1998
1999
// This performs a table alter, but does it unbuffered so the script can time out professionally.
2000
function protected_alter($change, $substep, $is_test = false)
2001
{
2002
	global $db_prefix, $smcFunc;
2003
2004
	db_extend('packages');
2005
2006
	// Firstly, check whether the current index/column exists.
2007
	$found = false;
2008
	if ($change['type'] === 'column')
2009
	{
2010
		$columns = $smcFunc['db_list_columns']('{db_prefix}' . $change['table'], true);
2011
		foreach ($columns as $column)
2012
		{
2013
			// Found it?
2014
			if ($column['name'] === $change['name'])
2015
			{
2016
				$found |= 1;
2017
				// Do some checks on the data if we have it set.
2018
				if (isset($change['col_type']))
2019
					$found &= $change['col_type'] === $column['type'];
2020
				if (isset($change['null_allowed']))
2021
					$found &= $column['null'] == $change['null_allowed'];
2022
				if (isset($change['default']))
2023
					$found &= $change['default'] === $column['default'];
2024
			}
2025
		}
2026
	}
2027
	elseif ($change['type'] === 'index')
2028
	{
2029
		$request = upgrade_query('
2030
			SHOW INDEX
2031
			FROM ' . $db_prefix . $change['table']);
2032
		if ($request !== false)
2033
		{
2034
			$cur_index = array();
2035
2036
			while ($row = $smcFunc['db_fetch_assoc']($request))
2037
				if ($row['Key_name'] === $change['name'])
2038
					$cur_index[(int) $row['Seq_in_index']] = $row['Column_name'];
2039
2040
			ksort($cur_index, SORT_NUMERIC);
2041
			$found = array_values($cur_index) === $change['target_columns'];
2042
2043
			$smcFunc['db_free_result']($request);
2044
		}
2045
	}
2046
2047
	// If we're trying to add and it's added, we're done.
2048
	if ($found && in_array($change['method'], array('add', 'change')))
2049
		return true;
2050
	// Otherwise if we're removing and it wasn't found we're also done.
2051
	elseif (!$found && in_array($change['method'], array('remove', 'change_remove')))
2052
		return true;
2053
	// Otherwise is it just a test?
2054
	elseif ($is_test)
2055
		return false;
2056
2057
	// Not found it yet? Bummer! How about we see if we're currently doing it?
2058
	$running = false;
2059
	$found = false;
2060
	while (1 == 1)
2061
	{
2062
		$request = upgrade_query('
2063
			SHOW FULL PROCESSLIST');
2064
		while ($row = $smcFunc['db_fetch_assoc']($request))
2065
		{
2066
			if (strpos($row['Info'], 'ALTER TABLE ' . $db_prefix . $change['table']) !== false && strpos($row['Info'], $change['text']) !== false)
2067
				$found = true;
2068
		}
2069
2070
		// Can't find it? Then we need to run it fools!
2071
		if (!$found && !$running)
2072
		{
2073
			$smcFunc['db_free_result']($request);
2074
2075
			$success = upgrade_query('
2076
				ALTER TABLE ' . $db_prefix . $change['table'] . '
2077
				' . $change['text'], true) !== false;
2078
2079
			if (!$success)
2080
				return false;
2081
2082
			// Return
2083
			$running = true;
2084
		}
2085
		// What if we've not found it, but we'd ran it already? Must of completed.
2086
		elseif (!$found)
2087
		{
2088
			$smcFunc['db_free_result']($request);
2089
			return true;
2090
		}
2091
2092
		// Pause execution for a sec or three.
2093
		sleep(3);
2094
2095
		// Can never be too well protected.
2096
		nextSubstep($substep);
2097
	}
2098
2099
	// Protect it.
2100
	nextSubstep($substep);
2101
}
2102
2103
/**
2104
 * Alter a text column definition preserving its character set.
2105
 *
2106
 * @param array $change
2107
 * @param int $substep
2108
 */
2109
function textfield_alter($change, $substep)
2110
{
2111
	global $db_prefix, $smcFunc;
2112
2113
	$request = $smcFunc['db_query']('', '
2114
		SHOW FULL COLUMNS
2115
		FROM {db_prefix}' . $change['table'] . '
2116
		LIKE {string:column}',
2117
		array(
2118
			'column' => $change['column'],
2119
			'db_error_skip' => true,
2120
		)
2121
	);
2122
	if ($smcFunc['db_num_rows']($request) === 0)
2123
		die('Unable to find column ' . $change['column'] . ' inside table ' . $db_prefix . $change['table']);
2124
	$table_row = $smcFunc['db_fetch_assoc']($request);
2125
	$smcFunc['db_free_result']($request);
2126
2127
	// If something of the current column definition is different, fix it.
2128
	$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']);
2129
2130
	// Columns that previously allowed null, need to be converted first.
2131
	$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
2132
2133
	// Get the character set that goes with the collation of the column.
2134 View Code Duplication
	if ($column_fix && !empty($table_row['Collation']))
2135
	{
2136
		$request = $smcFunc['db_query']('', '
2137
			SHOW COLLATION
2138
			LIKE {string:collation}',
2139
			array(
2140
				'collation' => $table_row['Collation'],
2141
				'db_error_skip' => true,
2142
			)
2143
		);
2144
		// No results? Just forget it all together.
2145
		if ($smcFunc['db_num_rows']($request) === 0)
2146
			unset($table_row['Collation']);
2147
		else
2148
			$collation_info = $smcFunc['db_fetch_assoc']($request);
2149
		$smcFunc['db_free_result']($request);
2150
	}
2151
2152
	if ($column_fix)
2153
	{
2154
		// Make sure there are no NULL's left.
2155
		if ($null_fix)
2156
			$smcFunc['db_query']('', '
2157
				UPDATE {db_prefix}' . $change['table'] . '
2158
				SET ' . $change['column'] . ' = {string:default}
2159
				WHERE ' . $change['column'] . ' IS NULL',
2160
				array(
2161
					'default' => isset($change['default']) ? $change['default'] : '',
2162
					'db_error_skip' => true,
2163
				)
2164
			);
2165
2166
		// Do the actual alteration.
2167
		$smcFunc['db_query']('', '
2168
			ALTER TABLE {db_prefix}' . $change['table'] . '
2169
			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}' : ''),
2170
			array(
2171
				'default' => isset($change['default']) ? $change['default'] : '',
2172
				'db_error_skip' => true,
2173
			)
2174
		);
2175
	}
2176
	nextSubstep($substep);
2177
}
2178
2179
// Check if we need to alter this query.
2180
function checkChange(&$change)
2181
{
2182
	global $smcFunc, $db_type, $databases;
2183
	static $database_version, $where_field_support;
2184
2185
	// Attempt to find a database_version.
2186
	if (empty($database_version))
2187
	{
2188
		$database_version = $databases[$db_type]['version_check'];
2189
		$where_field_support = $db_type == 'mysql' && version_compare('5.0', $database_version, '<=');
2190
	}
2191
2192
	// Not a column we need to check on?
2193
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
2194
		return;
2195
2196
	// Break it up you (six|seven).
2197
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
2198
2199
	// Can we support a shortcut method?
2200
	if ($where_field_support)
2201
	{
2202
		// Get the details about this change.
2203
		$request = $smcFunc['db_query']('', '
2204
			SHOW FIELDS
2205
			FROM {db_prefix}{raw:table}
2206
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
2207
			array(
2208
				'table' => $change['table'],
2209
				'old_name' => $temp[1],
2210
				'new_name' => $temp[2],
2211
		));
2212
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2213
		if ($smcFunc['db_num_rows'] != 1)
2214
			return;
2215
2216
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
2217
		$smcFunc['db_free_result']($request);
2218
	}
2219
	else
2220
	{
2221
		// Do this the old fashion, sure method way.
2222
		$request = $smcFunc['db_query']('', '
2223
			SHOW FIELDS
2224
			FROM {db_prefix}{raw:table}',
2225
			array(
2226
				'table' => $change['table'],
2227
		));
2228
		// Mayday!
2229
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2230
		if ($smcFunc['db_num_rows'] == 0)
2231
			return;
2232
2233
		// Oh where, oh where has my little field gone. Oh where can it be...
2234
		while ($row = $smcFunc['db_query']($request))
2235
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
2236
			{
2237
				$current_type = $row['Type'];
2238
				break;
2239
			}
2240
	}
2241
2242
	// If this doesn't match, the column may of been altered for a reason.
2243
	if (trim($current_type) != trim($temp[3]))
2244
		$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...
2245
2246
	// Piece this back together.
2247
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
2248
}
2249
2250
// The next substep.
2251
function nextSubstep($substep)
2252
{
2253
	global $start_time, $timeLimitThreshold, $command_line, $custom_warning;
2254
	global $step_progress, $is_debug, $upcontext;
2255
2256
	if ($_GET['substep'] < $substep)
2257
		$_GET['substep'] = $substep;
2258
2259
	if ($command_line)
2260
	{
2261
		if (time() - $start_time > 1 && empty($is_debug))
2262
		{
2263
			echo '.';
2264
			$start_time = time();
2265
		}
2266
		return;
2267
	}
2268
2269
	@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...
2270
	if (function_exists('apache_reset_timeout'))
2271
		@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...
2272
2273
	if (time() - $start_time <= $timeLimitThreshold)
2274
		return;
2275
2276
	// Do we have some custom step progress stuff?
2277
	if (!empty($step_progress))
2278
	{
2279
		$upcontext['substep_progress'] = 0;
2280
		$upcontext['substep_progress_name'] = $step_progress['name'];
2281
		if ($step_progress['current'] > $step_progress['total'])
2282
			$upcontext['substep_progress'] = 99.9;
2283
		else
2284
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2285
2286
		// Make it nicely rounded.
2287
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2288
	}
2289
2290
	// If this is XML we just exit right away!
2291
	if (isset($_GET['xml']))
2292
		return upgradeExit();
2293
2294
	// We're going to pause after this!
2295
	$upcontext['pause'] = true;
2296
2297
	$upcontext['query_string'] = '';
2298
	foreach ($_GET as $k => $v)
2299
	{
2300
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2301
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2302
	}
2303
2304
	// Custom warning?
2305
	if (!empty($custom_warning))
2306
		$upcontext['custom_warning'] = $custom_warning;
2307
2308
	upgradeExit();
2309
}
2310
2311
function cmdStep0()
2312
{
2313
	global $boarddir, $sourcedir, $modSettings, $start_time, $cachedir, $databases, $db_type, $smcFunc, $upcontext;
2314
	global $is_debug;
2315
	$start_time = time();
2316
2317
	ob_end_clean();
2318
	ob_implicit_flush(true);
2319
	@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...
2320
2321
	if (!isset($_SERVER['argv']))
2322
		$_SERVER['argv'] = array();
2323
	$_GET['maint'] = 1;
2324
2325
	foreach ($_SERVER['argv'] as $i => $arg)
2326
	{
2327
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2328
			$_GET['lang'] = $match[1];
2329
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2330
			continue;
2331
		elseif ($arg == '--no-maintenance')
2332
			$_GET['maint'] = 0;
2333
		elseif ($arg == '--debug')
2334
			$is_debug = true;
2335
		elseif ($arg == '--backup')
2336
			$_POST['backup'] = 1;
2337
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2338
			$_GET['conv'] = 1;
2339
		elseif ($i != 0)
2340
		{
2341
			echo 'SMF Command-line Upgrader
2342
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2343
2344
    --language=LANG         Reset the forum\'s language to LANG.
2345
    --no-maintenance        Don\'t put the forum into maintenance mode.
2346
    --debug                 Output debugging information.
2347
    --backup                Create backups of tables with "backup_" prefix.';
2348
			echo "\n";
2349
			exit;
2350
		}
2351
	}
2352
2353
	if (!php_version_check())
2354
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
2355
	if (!db_version_check())
2356
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2357
2358
	// Do some checks to make sure they have proper privileges
2359
	db_extend('packages');
2360
2361
	// CREATE
2362
	$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');
2363
2364
	// ALTER
2365
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'tinytext', 'null' => false, 'default' => ''));
2366
2367
	// DROP
2368
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2369
2370
	// Sorry... we need CREATE, ALTER and DROP
2371 View Code Duplication
	if (!$create || !$alter || !$drop)
2372
		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);
2373
2374
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2375
		&& @file_exists($sourcedir . '/QueryString.php')
2376
		&& @file_exists($sourcedir . '/ManageBoards.php');
2377
	if (!$check && !isset($modSettings['smfVersion']))
2378
		print_error('Error: Some files are missing or out-of-date.', true);
2379
2380
	// Do a quick version spot check.
2381
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
2382
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2383
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2384
		print_error('Error: Some files have not yet been updated properly.');
2385
2386
	// Make sure Settings.php is writable.
2387
		quickFileWritable($boarddir . '/Settings.php');
2388
	if (!is_writable($boarddir . '/Settings.php'))
2389
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2390
2391
	// Make sure Settings_bak.php is writable.
2392
		quickFileWritable($boarddir . '/Settings_bak.php');
2393
	if (!is_writable($boarddir . '/Settings_bak.php'))
2394
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2395
2396 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
2397
		print_error('Error: Unable to obtain write access to "agreement.txt".');
2398
	elseif (isset($modSettings['agreement']))
2399
	{
2400
		$fp = fopen($boarddir . '/agreement.txt', 'w');
2401
		fwrite($fp, $modSettings['agreement']);
2402
		fclose($fp);
2403
	}
2404
2405
	// Make sure Themes is writable.
2406
	quickFileWritable($modSettings['theme_dir']);
2407
2408
	if (!is_writable($modSettings['theme_dir']) && !isset($modSettings['smfVersion']))
2409
		print_error('Error: Unable to obtain write access to "Themes".');
2410
2411
	// Make sure cache directory exists and is writable!
2412
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
2413
	if (!file_exists($cachedir_temp))
2414
		@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...
2415
2416
	// Make sure the cache temp dir is writable.
2417
	quickFileWritable($cachedir_temp);
2418
2419
	if (!is_writable($cachedir_temp))
2420
		print_error('Error: Unable to obtain write access to "cache".', true);
2421
2422
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
2423
		print_error('Error: Unable to find language files!', true);
2424
	else
2425
	{
2426
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
2427
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
2428
2429
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
2430
			print_error('Error: Language files out of date.', true);
2431
		if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
2432
			print_error('Error: Install language is missing for selected language.', true);
2433
2434
		// Otherwise include it!
2435
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
2436
	}
2437
2438
	// Make sure we skip the HTML for login.
2439
	$_POST['upcont'] = true;
2440
	$upcontext['current_step'] = 1;
2441
}
2442
2443
/**
2444
 * Handles converting your database to UTF-8
2445
 */
2446
function ConvertUtf8()
2447
{
2448
	global $upcontext, $db_character_set, $sourcedir, $smcFunc, $modSettings, $language, $db_prefix, $db_type, $command_line, $support_js;
2449
2450
	// Done it already?
2451
	if (!empty($_POST['utf8_done']))
2452
		return true;
2453
2454
	// First make sure they aren't already on UTF-8 before we go anywhere...
2455
	if ($db_type == 'postgresql' || ($db_character_set === 'utf8' && !empty($modSettings['global_character_set']) && $modSettings['global_character_set'] === 'UTF-8'))
2456
	{
2457
		$smcFunc['db_insert']('replace',
2458
			'{db_prefix}settings',
2459
			array('variable' => 'string', 'value' => 'string'),
2460
			array(array('global_character_set', 'UTF-8')),
2461
			array('variable')
2462
		);
2463
2464
		return true;
2465
	}
2466
	else
2467
	{
2468
		$upcontext['page_title'] = 'Converting to UTF8';
2469
		$upcontext['sub_template'] = isset($_GET['xml']) ? 'convert_xml' : 'convert_utf8';
2470
2471
		// The character sets used in SMF's language files with their db equivalent.
2472
		$charsets = array(
2473
			// Armenian
2474
			'armscii8' => 'armscii8',
2475
			// Chinese-traditional.
2476
			'big5' => 'big5',
2477
			// Chinese-simplified.
2478
			'gbk' => 'gbk',
2479
			// West European.
2480
			'ISO-8859-1' => 'latin1',
2481
			// Romanian.
2482
			'ISO-8859-2' => 'latin2',
2483
			// Turkish.
2484
			'ISO-8859-9' => 'latin5',
2485
			// Latvian
2486
			'ISO-8859-13' => 'latin7',
2487
			// West European with Euro sign.
2488
			'ISO-8859-15' => 'latin9',
2489
			// Thai.
2490
			'tis-620' => 'tis620',
2491
			// Persian, Chinese, etc.
2492
			'UTF-8' => 'utf8',
2493
			// Russian.
2494
			'windows-1251' => 'cp1251',
2495
			// Greek.
2496
			'windows-1253' => 'utf8',
2497
			// Hebrew.
2498
			'windows-1255' => 'utf8',
2499
			// Arabic.
2500
			'windows-1256' => 'cp1256',
2501
		);
2502
2503
		// Get a list of character sets supported by your MySQL server.
2504
		$request = $smcFunc['db_query']('', '
2505
			SHOW CHARACTER SET',
2506
			array(
2507
			)
2508
		);
2509
		$db_charsets = array();
2510
		while ($row = $smcFunc['db_fetch_assoc']($request))
2511
			$db_charsets[] = $row['Charset'];
2512
2513
		$smcFunc['db_free_result']($request);
2514
2515
		// Character sets supported by both MySQL and SMF's language files.
2516
		$charsets = array_intersect($charsets, $db_charsets);
2517
2518
		// Use the messages.body column as indicator for the database charset.
2519
		$request = $smcFunc['db_query']('', '
2520
			SHOW FULL COLUMNS
2521
			FROM {db_prefix}messages
2522
			LIKE {string:body_like}',
2523
			array(
2524
				'body_like' => 'body',
2525
			)
2526
		);
2527
		$column_info = $smcFunc['db_fetch_assoc']($request);
2528
		$smcFunc['db_free_result']($request);
2529
2530
		// A collation looks like latin1_swedish. We only need the character set.
2531
		list($upcontext['database_charset']) = explode('_', $column_info['Collation']);
2532
		$upcontext['database_charset'] = in_array($upcontext['database_charset'], $charsets) ? array_search($upcontext['database_charset'], $charsets) : $upcontext['database_charset'];
2533
2534
		// Detect whether a fulltext index is set.
2535
		$request = $smcFunc['db_query']('', '
2536
 			SHOW INDEX
2537
	  	    FROM {db_prefix}messages',
2538
			array(
2539
			)
2540
		);
2541
2542
		$upcontext['dropping_index'] = false;
2543
2544
		// If there's a fulltext index, we need to drop it first...
2545 View Code Duplication
		if ($request !== false || $smcFunc['db_num_rows']($request) != 0)
2546
		{
2547
			while ($row = $smcFunc['db_fetch_assoc']($request))
2548
				if ($row['Column_name'] == 'body' && (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT' || isset($row['Comment']) && $row['Comment'] == 'FULLTEXT'))
2549
					$upcontext['fulltext_index'][] = $row['Key_name'];
2550
			$smcFunc['db_free_result']($request);
2551
2552
			if (isset($upcontext['fulltext_index']))
2553
				$upcontext['fulltext_index'] = array_unique($upcontext['fulltext_index']);
2554
		}
2555
2556
		// Drop it and make a note...
2557
		if (!empty($upcontext['fulltext_index']))
2558
		{
2559
			$upcontext['dropping_index'] = true;
2560
2561
			$smcFunc['db_query']('', '
2562
  			ALTER TABLE {db_prefix}messages
2563
	  		DROP INDEX ' . implode(',
2564
		  	DROP INDEX ', $upcontext['fulltext_index']),
2565
				array(
2566
					'db_error_skip' => true,
2567
				)
2568
			);
2569
2570
			// Update the settings table
2571
			$smcFunc['db_insert']('replace',
2572
				'{db_prefix}settings',
2573
				array('variable' => 'string', 'value' => 'string'),
2574
				array('db_search_index', ''),
2575
				array('variable')
2576
			);
2577
		}
2578
2579
		// Figure out what charset we should be converting from...
2580
		$lang_charsets = array(
2581
			'arabic' => 'windows-1256',
2582
			'armenian_east' => 'armscii-8',
2583
			'armenian_west' => 'armscii-8',
2584
			'azerbaijani_latin' => 'ISO-8859-9',
2585
			'bangla' => 'UTF-8',
2586
			'belarusian' => 'ISO-8859-5',
2587
			'bulgarian' => 'windows-1251',
2588
			'cambodian' => 'UTF-8',
2589
			'chinese_simplified' => 'gbk',
2590
			'chinese_traditional' => 'big5',
2591
			'croation' => 'ISO-8859-2',
2592
			'czech' => 'ISO-8859-2',
2593
			'czech_informal' => 'ISO-8859-2',
2594
			'english_pirate' => 'UTF-8',
2595
			'esperanto' => 'ISO-8859-3',
2596
			'estonian' => 'ISO-8859-15',
2597
			'filipino_tagalog' => 'UTF-8',
2598
			'filipino_vasayan' => 'UTF-8',
2599
			'georgian' => 'UTF-8',
2600
			'greek' => 'ISO-8859-3',
2601
			'hebrew' => 'windows-1255',
2602
			'hungarian' => 'ISO-8859-2',
2603
			'irish' => 'UTF-8',
2604
			'japanese' => 'UTF-8',
2605
			'khmer' => 'UTF-8',
2606
			'korean' => 'UTF-8',
2607
			'kurdish_kurmanji' => 'ISO-8859-9',
2608
			'kurdish_sorani' => 'windows-1256',
2609
			'lao' => 'tis-620',
2610
			'latvian' => 'ISO-8859-13',
2611
			'lithuanian' => 'ISO-8859-4',
2612
			'macedonian' => 'UTF-8',
2613
			'malayalam' => 'UTF-8',
2614
			'mongolian' => 'UTF-8',
2615
			'nepali' => 'UTF-8',
2616
			'persian' => 'UTF-8',
2617
			'polish' => 'ISO-8859-2',
2618
			'romanian' => 'ISO-8859-2',
2619
			'russian' => 'windows-1252',
2620
			'sakha' => 'UTF-8',
2621
			'serbian_cyrillic' => 'ISO-8859-5',
2622
			'serbian_latin' => 'ISO-8859-2',
2623
			'sinhala' => 'UTF-8',
2624
			'slovak' => 'ISO-8859-2',
2625
			'slovenian' => 'ISO-8859-2',
2626
			'telugu' => 'UTF-8',
2627
			'thai' => 'tis-620',
2628
			'turkish' => 'ISO-8859-9',
2629
			'turkmen' => 'ISO-8859-9',
2630
			'ukranian' => 'windows-1251',
2631
			'urdu' => 'UTF-8',
2632
			'uzbek_cyrillic' => 'ISO-8859-5',
2633
			'uzbek_latin' => 'ISO-8859-5',
2634
			'vietnamese' => 'UTF-8',
2635
			'yoruba' => 'UTF-8'
2636
		);
2637
2638
		// Default to ISO-8859-1 unless we detected another supported charset
2639
		$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';
2640
2641
		$upcontext['charset_list'] = array_keys($charsets);
2642
2643
		// Translation table for the character sets not native for MySQL.
2644
		$translation_tables = array(
2645
			'windows-1255' => array(
2646
				'0x81' => '\'\'',		'0x8A' => '\'\'',		'0x8C' => '\'\'',
2647
				'0x8D' => '\'\'',		'0x8E' => '\'\'',		'0x8F' => '\'\'',
2648
				'0x90' => '\'\'',		'0x9A' => '\'\'',		'0x9C' => '\'\'',
2649
				'0x9D' => '\'\'',		'0x9E' => '\'\'',		'0x9F' => '\'\'',
2650
				'0xCA' => '\'\'',		'0xD9' => '\'\'',		'0xDA' => '\'\'',
2651
				'0xDB' => '\'\'',		'0xDC' => '\'\'',		'0xDD' => '\'\'',
2652
				'0xDE' => '\'\'',		'0xDF' => '\'\'',		'0xFB' => '0xD792',
2653
				'0xFC' => '0xE282AC',		'0xFF' => '0xD6B2',		'0xC2' => '0xFF',
2654
				'0x80' => '0xFC',		'0xE2' => '0xFB',		'0xA0' => '0xC2A0',
2655
				'0xA1' => '0xC2A1',		'0xA2' => '0xC2A2',		'0xA3' => '0xC2A3',
2656
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2657
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2658
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2659
				'0xAF' => '0xC2AF',		'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',
2660
				'0xB2' => '0xC2B2',		'0xB3' => '0xC2B3',		'0xB4' => '0xC2B4',
2661
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2662
				'0xB8' => '0xC2B8',		'0xB9' => '0xC2B9',		'0xBB' => '0xC2BB',
2663
				'0xBC' => '0xC2BC',		'0xBD' => '0xC2BD',		'0xBE' => '0xC2BE',
2664
				'0xBF' => '0xC2BF',		'0xD7' => '0xD7B3',		'0xD1' => '0xD781',
2665
				'0xD4' => '0xD7B0',		'0xD5' => '0xD7B1',		'0xD6' => '0xD7B2',
2666
				'0xE0' => '0xD790',		'0xEA' => '0xD79A',		'0xEC' => '0xD79C',
2667
				'0xED' => '0xD79D',		'0xEE' => '0xD79E',		'0xEF' => '0xD79F',
2668
				'0xF0' => '0xD7A0',		'0xF1' => '0xD7A1',		'0xF2' => '0xD7A2',
2669
				'0xF3' => '0xD7A3',		'0xF5' => '0xD7A5',		'0xF6' => '0xD7A6',
2670
				'0xF7' => '0xD7A7',		'0xF8' => '0xD7A8',		'0xF9' => '0xD7A9',
2671
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2672
				'0x86' => '0xE280A0',	'0x87' => '0xE280A1',	'0x89' => '0xE280B0',
2673
				'0x8B' => '0xE280B9',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2674
				'0x95' => '0xE280A2',	'0x97' => '0xE28094',	'0x99' => '0xE284A2',
2675
				'0xC0' => '0xD6B0',		'0xC1' => '0xD6B1',		'0xC3' => '0xD6B3',
2676
				'0xC4' => '0xD6B4',		'0xC5' => '0xD6B5',		'0xC6' => '0xD6B6',
2677
				'0xC7' => '0xD6B7',		'0xC8' => '0xD6B8',		'0xC9' => '0xD6B9',
2678
				'0xCB' => '0xD6BB',		'0xCC' => '0xD6BC',		'0xCD' => '0xD6BD',
2679
				'0xCE' => '0xD6BE',		'0xCF' => '0xD6BF',		'0xD0' => '0xD780',
2680
				'0xD2' => '0xD782',		'0xE3' => '0xD793',		'0xE4' => '0xD794',
2681
				'0xE5' => '0xD795',		'0xE7' => '0xD797',		'0xE9' => '0xD799',
2682
				'0xFD' => '0xE2808E',	'0xFE' => '0xE2808F',	'0x92' => '0xE28099',
2683
				'0x83' => '0xC692',		'0xD3' => '0xD783',		'0x88' => '0xCB86',
2684
				'0x98' => '0xCB9C',		'0x91' => '0xE28098',	'0x96' => '0xE28093',
2685
				'0xBA' => '0xC3B7',		'0x9B' => '0xE280BA',	'0xAA' => '0xC397',
2686
				'0xA4' => '0xE282AA',	'0xE1' => '0xD791',		'0xE6' => '0xD796',
2687
				'0xE8' => '0xD798',		'0xEB' => '0xD79B',		'0xF4' => '0xD7A4',
2688
				'0xFA' => '0xD7AA',
2689
			),
2690
			'windows-1253' => array(
2691
				'0x81' => '\'\'',			'0x88' => '\'\'',			'0x8A' => '\'\'',
2692
				'0x8C' => '\'\'',			'0x8D' => '\'\'',			'0x8E' => '\'\'',
2693
				'0x8F' => '\'\'',			'0x90' => '\'\'',			'0x98' => '\'\'',
2694
				'0x9A' => '\'\'',			'0x9C' => '\'\'',			'0x9D' => '\'\'',
2695
				'0x9E' => '\'\'',			'0x9F' => '\'\'',			'0xAA' => '\'\'',
2696
				'0xD2' => '0xE282AC',			'0xFF' => '0xCE92',			'0xCE' => '0xCE9E',
2697
				'0xB8' => '0xCE88',		'0xBA' => '0xCE8A',		'0xBC' => '0xCE8C',
2698
				'0xBE' => '0xCE8E',		'0xBF' => '0xCE8F',		'0xC0' => '0xCE90',
2699
				'0xC8' => '0xCE98',		'0xCA' => '0xCE9A',		'0xCC' => '0xCE9C',
2700
				'0xCD' => '0xCE9D',		'0xCF' => '0xCE9F',		'0xDA' => '0xCEAA',
2701
				'0xE8' => '0xCEB8',		'0xEA' => '0xCEBA',		'0xEC' => '0xCEBC',
2702
				'0xEE' => '0xCEBE',		'0xEF' => '0xCEBF',		'0xC2' => '0xFF',
2703
				'0xBD' => '0xC2BD',		'0xED' => '0xCEBD',		'0xB2' => '0xC2B2',
2704
				'0xA0' => '0xC2A0',		'0xA3' => '0xC2A3',		'0xA4' => '0xC2A4',
2705
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2706
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2707
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2708
				'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',		'0xB3' => '0xC2B3',
2709
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2710
				'0xBB' => '0xC2BB',		'0xE2' => '0xCEB2',		'0x80' => '0xD2',
2711
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2712
				'0x86' => '0xE280A0',	'0xA1' => '0xCE85',		'0xA2' => '0xCE86',
2713
				'0x87' => '0xE280A1',	'0x89' => '0xE280B0',	'0xB9' => '0xCE89',
2714
				'0x8B' => '0xE280B9',	'0x91' => '0xE28098',	'0x99' => '0xE284A2',
2715
				'0x92' => '0xE28099',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2716
				'0x95' => '0xE280A2',	'0x96' => '0xE28093',	'0x97' => '0xE28094',
2717
				'0x9B' => '0xE280BA',	'0xAF' => '0xE28095',	'0xB4' => '0xCE84',
2718
				'0xC1' => '0xCE91',		'0xC3' => '0xCE93',		'0xC4' => '0xCE94',
2719
				'0xC5' => '0xCE95',		'0xC6' => '0xCE96',		'0x83' => '0xC692',
2720
				'0xC7' => '0xCE97',		'0xC9' => '0xCE99',		'0xCB' => '0xCE9B',
2721
				'0xD0' => '0xCEA0',		'0xD1' => '0xCEA1',		'0xD3' => '0xCEA3',
2722
				'0xD4' => '0xCEA4',		'0xD5' => '0xCEA5',		'0xD6' => '0xCEA6',
2723
				'0xD7' => '0xCEA7',		'0xD8' => '0xCEA8',		'0xD9' => '0xCEA9',
2724
				'0xDB' => '0xCEAB',		'0xDC' => '0xCEAC',		'0xDD' => '0xCEAD',
2725
				'0xDE' => '0xCEAE',		'0xDF' => '0xCEAF',		'0xE0' => '0xCEB0',
2726
				'0xE1' => '0xCEB1',		'0xE3' => '0xCEB3',		'0xE4' => '0xCEB4',
2727
				'0xE5' => '0xCEB5',		'0xE6' => '0xCEB6',		'0xE7' => '0xCEB7',
2728
				'0xE9' => '0xCEB9',		'0xEB' => '0xCEBB',		'0xF0' => '0xCF80',
2729
				'0xF1' => '0xCF81',		'0xF2' => '0xCF82',		'0xF3' => '0xCF83',
2730
				'0xF4' => '0xCF84',		'0xF5' => '0xCF85',		'0xF6' => '0xCF86',
2731
				'0xF7' => '0xCF87',		'0xF8' => '0xCF88',		'0xF9' => '0xCF89',
2732
				'0xFA' => '0xCF8A',		'0xFB' => '0xCF8B',		'0xFC' => '0xCF8C',
2733
				'0xFD' => '0xCF8D',		'0xFE' => '0xCF8E',
2734
			),
2735
		);
2736
2737
		// Make some preparations.
2738
		if (isset($translation_tables[$upcontext['charset_detected']]))
2739
		{
2740
			$replace = '%field%';
2741
2742
			// Build a huge REPLACE statement...
2743
			foreach ($translation_tables[$upcontext['charset_detected']] as $from => $to)
2744
				$replace = 'REPLACE(' . $replace . ', ' . $from . ', ' . $to . ')';
2745
		}
2746
2747
		// Get a list of table names ahead of time... This makes it easier to set our substep and such
2748
		db_extend();
2749
		$queryTables = $smcFunc['db_list_tables'](false, $db_prefix . '%');
2750
2751
		$upcontext['table_count'] = count($queryTables);
2752
	
2753
		// What ones have we already done?
2754
		foreach ($queryTables as $id => $table)
2755
			if ($id < $_GET['substep'])
2756
				$upcontext['previous_tables'][] = $table;
2757
2758
		$upcontext['cur_table_num'] = $_GET['substep'];
2759
		$upcontext['cur_table_name'] = str_replace($db_prefix, '', $queryTables[$_GET['substep']]);
2760
		$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2761
			
2762
		// Make sure we're ready & have painted the template before proceeding	
2763
		if ($support_js && !isset($_GET['xml'])) {
2764
			$_GET['substep'] = 0;
2765
			return false;
2766
		}	
2767
			
2768
		// We want to start at the first table.
2769
		for ($substep = $_GET['substep'], $n = count($queryTables); $substep < $n; $substep++)
2770
		{
2771
			$table = $queryTables[$substep];
2772
2773
			$getTableStatus = $smcFunc['db_query']('', '
2774
				SHOW TABLE STATUS
2775
				LIKE {string:table_name}',
2776
				array(
2777
					'table_name' => str_replace('_', '\_', $table)
2778
				)
2779
			);
2780
2781
			// Only one row so we can just fetch_assoc and free the result...
2782
			$table_info = $smcFunc['db_fetch_assoc']($getTableStatus);
2783
			$smcFunc['db_free_result']($getTableStatus);
2784
2785
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($queryTables[$substep + 1]) ? $queryTables[$substep + 1] : $queryTables[$substep]));
2786
			$upcontext['cur_table_num'] = $substep + 1;
2787
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2788
2789
			// Do we need to pause?
2790
			nextSubstep($substep);
2791
2792
			// Just to make sure it doesn't time out.
2793
			if (function_exists('apache_reset_timeout'))
2794
				@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...
2795
2796
			$table_charsets = array();
2797
2798
			// Loop through each column.
2799
			$queryColumns = $smcFunc['db_query']('', '
2800
				SHOW FULL COLUMNS
2801
				FROM ' . $table_info['Name'],
2802
				array(
2803
				)
2804
			);
2805
			while ($column_info = $smcFunc['db_fetch_assoc']($queryColumns))
2806
			{
2807
				// Only text'ish columns have a character set and need converting.
2808
				if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false)
2809
				{
2810
					$collation = empty($column_info['Collation']) || $column_info['Collation'] === 'NULL' ? $table_info['Collation'] : $column_info['Collation'];
2811
					if (!empty($collation) && $collation !== 'NULL')
2812
					{
2813
						list($charset) = explode('_', $collation);
2814
2815
						if (!isset($table_charsets[$charset]))
2816
							$table_charsets[$charset] = array();
2817
2818
						$table_charsets[$charset][] = $column_info;
2819
					}
2820
				}
2821
			}
2822
			$smcFunc['db_free_result']($queryColumns);
2823
2824
			// Only change the column if the data doesn't match the current charset.
2825
			if ((count($table_charsets) === 1 && key($table_charsets) !== $charsets[$upcontext['charset_detected']]) || count($table_charsets) > 1)
2826
			{
2827
				$updates_blob = '';
2828
				$updates_text = '';
2829
				foreach ($table_charsets as $charset => $columns)
2830
				{
2831
					if ($charset !== $charsets[$upcontext['charset_detected']])
2832
					{
2833
						foreach ($columns as $column)
2834
						{
2835
							$updates_blob .= '
2836
								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'] . '\'') . ',';
2837
							$updates_text .= '
2838
								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'] . '\'') . ',';
2839
						}
2840
					}
2841
				}
2842
2843
				// Change the columns to binary form.
2844
				$smcFunc['db_query']('', '
2845
					ALTER TABLE {raw:table_name}{raw:updates_blob}',
2846
					array(
2847
						'table_name' => $table_info['Name'],
2848
						'updates_blob' => substr($updates_blob, 0, -1),
2849
					)
2850
				);
2851
2852
				// Convert the character set if MySQL has no native support for it.
2853
				if (isset($translation_tables[$upcontext['charset_detected']]))
2854
				{
2855
					$update = '';
2856
					foreach ($table_charsets as $charset => $columns)
2857
						foreach ($columns as $column)
2858
							$update .= '
2859
								' . $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...
2860
2861
					$smcFunc['db_query']('', '
2862
						UPDATE {raw:table_name}
2863
						SET {raw:updates}',
2864
						array(
2865
							'table_name' => $table_info['Name'],
2866
							'updates' => substr($update, 0, -1),
2867
						)
2868
					);
2869
				}
2870
2871
				// Change the columns back, but with the proper character set.
2872
				$smcFunc['db_query']('', '
2873
					ALTER TABLE {raw:table_name}{raw:updates_text}',
2874
					array(
2875
						'table_name' => $table_info['Name'],
2876
						'updates_text' => substr($updates_text, 0, -1),
2877
					)
2878
				);
2879
			}
2880
2881
			// Now do the actual conversion (if still needed).
2882
			if ($charsets[$upcontext['charset_detected']] !== 'utf8')
2883
			{
2884
				if ($command_line)
2885
					echo 'Converting table ' . $table_info['Name'] . ' to UTF-8...';
2886
2887
				$smcFunc['db_query']('', '
2888
					ALTER TABLE {raw:table_name}
2889
					CONVERT TO CHARACTER SET utf8',
2890
					array(
2891
						'table_name' => $table_info['Name'],
2892
					)
2893
				);
2894
2895
				if ($command_line)
2896
					echo " done.\n";
2897
			}
2898
			// If this is XML to keep it nice for the user do one table at a time anyway!
2899
			if (isset($_GET['xml']))
2900
				return upgradeExit();
2901
		}
2902
2903
		$prev_charset = empty($translation_tables[$upcontext['charset_detected']]) ? $charsets[$upcontext['charset_detected']] : $translation_tables[$upcontext['charset_detected']];
2904
2905
		$smcFunc['db_insert']('replace',
2906
			'{db_prefix}settings',
2907
			array('variable' => 'string', 'value' => 'string'),
2908
			array(array('global_character_set', 'UTF-8'), array('previousCharacterSet', $prev_charset)),
2909
			array('variable')
2910
		);
2911
2912
		// Store it in Settings.php too because it's needed before db connection.
2913
		// Hopefully this works...
2914
		require_once($sourcedir . '/Subs-Admin.php');
2915
		updateSettingsFile(array('db_character_set' => '\'utf8\''));
2916
2917
		// The conversion might have messed up some serialized strings. Fix them!
2918
		$request = $smcFunc['db_query']('', '
2919
			SELECT id_action, extra
2920
			FROM {db_prefix}log_actions
2921
			WHERE action IN ({string:remove}, {string:delete})',
2922
			array(
2923
				'remove' => 'remove',
2924
				'delete' => 'delete',
2925
			)
2926
		);
2927 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
2928
		{
2929
			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)
2930
				$smcFunc['db_query']('', '
2931
					UPDATE {db_prefix}log_actions
2932
					SET extra = {string:extra}
2933
					WHERE id_action = {int:current_action}',
2934
					array(
2935
						'current_action' => $row['id_action'],
2936
						'extra' => $matches[1] . strlen($matches[3]) . ':"' . $matches[3] . '"' . $matches[4],
2937
					)
2938
				);
2939
		}
2940
		$smcFunc['db_free_result']($request);
2941
2942
		if ($upcontext['dropping_index'] && $command_line)
2943
		{
2944
			echo "\nYour fulltext search index was dropped to facilitate the conversion. You will need to recreate it.";
2945
			flush();
2946
		}
2947
	}
2948
	$_GET['substep'] = 0;
2949
	return false;
2950
}
2951
2952
function serialize_to_json()
2953
{
2954
	global $command_line, $smcFunc, $modSettings, $sourcedir, $upcontext, $support_js;
2955
2956
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'serialize_json_xml' : 'serialize_json';
2957
	// First thing's first - did we already do this?
2958
	if (!empty($modSettings['json_done']))
2959
	{
2960
		if ($command_line)
2961
			return DeleteUpgrade();
2962
		else
2963
			return true;
2964
	}
2965
2966
	// Done it already - js wise?
2967
	if (!empty($_POST['json_done']))
2968
		return true;
2969
2970
	// List of tables affected by this function
2971
	// 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...
2972
	// If 3rd item in array is true, it indicates that col1 could be empty...
2973
	$tables = array(
2974
		'background_tasks' => array('id_task', 'task_data'),
2975
		'log_actions' => array('id_action', 'extra'),
2976
		'log_online' => array('session', 'url'),
2977
		'log_packages' => array('id_install', 'db_changes', 'failed_steps', 'credits'),
2978
		'log_spider_hits' => array('id_hit', 'url'),
2979
		'log_subscribed' => array('id_sublog', 'pending_details'),
2980
		'pm_rules' => array('id_rule', 'criteria', 'actions'),
2981
		'qanda' => array('id_question', 'answers'),
2982
		'subscriptions' => array('id_subscribe', 'cost'),
2983
		'user_alerts' => array('id_alert', 'extra', true),
2984
		'user_drafts' => array('id_draft', 'to_list', true),
2985
		// These last two are a bit different - we'll handle those separately
2986
		'settings' => array(),
2987
		'themes' => array()
2988
	);
2989
2990
	// Set up some context stuff...
2991
	// Because we're not using numeric indices, we need this to figure out the current table name...
2992
	$keys = array_keys($tables);
2993
2994
	$upcontext['page_title'] = 'Converting to JSON';
2995
	$upcontext['table_count'] = count($keys);
2996
	$upcontext['cur_table_num'] = $_GET['substep'];
2997
	$upcontext['cur_table_name'] = isset($keys[$_GET['substep']]) ? $keys[$_GET['substep']] : $keys[0];
2998
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2999
3000 View Code Duplication
	foreach ($keys as $id => $table)
3001
		if ($id < $_GET['substep'])
3002
			$upcontext['previous_tables'][] = $table;
3003
3004
	if ($command_line)
3005
		echo 'Converting data from serialize() to json_encode().';
3006
3007
	if (!$support_js || isset($_GET['xml']))
3008
	{
3009
		// Fix the data in each table
3010
		for ($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
3011
		{
3012
			$upcontext['cur_table_name'] = isset($keys[$substep + 1]) ? $keys[$substep + 1] : $keys[$substep];
3013
			$upcontext['cur_table_num'] = $substep + 1;
3014
3015
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3016
3017
			// Do we need to pause?
3018
			nextSubstep($substep);
3019
3020
			// Initialize a few things...
3021
			$where = '';
3022
			$vars = array();
3023
			$table = $keys[$substep];
3024
			$info = $tables[$table];
3025
3026
			// Now the fun - build our queries and all that fun stuff
3027
			if ($table == 'settings')
3028
			{
3029
				// Now a few settings...
3030
				$serialized_settings = array(
3031
					'attachment_basedirectories',
3032
					'attachmentUploadDir',
3033
					'cal_today_birthday',
3034
					'cal_today_event',
3035
					'cal_today_holiday',
3036
					'displayFields',
3037
					'last_attachments_directory',
3038
					'memberlist_cache',
3039
					'search_index_custom_config',
3040
					'spider_name_cache'
3041
				);
3042
3043
				// Loop through and fix these...
3044
				$new_settings = array();
3045
				if ($command_line)
3046
					echo "\n" . 'Fixing some settings...';
3047
3048
				foreach ($serialized_settings as $var)
3049
				{
3050
					if (isset($modSettings[$var]))
3051
					{
3052
						// Attempt to unserialize the setting
3053
						$temp = @safe_unserialize($modSettings[$var]);
3054
						if (!$temp && $command_line)
3055
							echo "\n - Failed to unserialize the '" . $var . "' setting. Skipping.";
3056
						elseif ($temp !== false)
3057
							$new_settings[$var] = json_encode($temp);
3058
					}
3059
				}
3060
3061
				// Update everything at once
3062
				if (!function_exists('cache_put_data'))
3063
					require_once($sourcedir . '/Load.php');
3064
				updateSettings($new_settings, true);
3065
3066
				if ($command_line)
3067
					echo ' done.';
3068
			}
3069
			elseif ($table == 'themes')
3070
			{
3071
				// Finally, fix the admin prefs. Unfortunately this is stored per theme, but hopefully they only have one theme installed at this point...
3072
				$query = $smcFunc['db_query']('', '
3073
					SELECT id_member, id_theme, value FROM {db_prefix}themes
3074
					WHERE variable = {string:admin_prefs}',
3075
						array(
3076
							'admin_prefs' => 'admin_preferences'
3077
						)
3078
				);
3079
3080
				if ($smcFunc['db_num_rows']($query) != 0)
3081
				{
3082
					while ($row = $smcFunc['db_fetch_assoc']($query))
3083
					{
3084
						$temp = @safe_unserialize($row['value']);
3085
3086
						if ($command_line)
3087
						{
3088
							if ($temp === false)
3089
								echo "\n" . 'Unserialize of admin_preferences for user ' . $row['id_member'] . ' failed. Skipping.';
3090
							else
3091
								echo "\n" . 'Fixing admin preferences...';
3092
						}
3093
3094
						if ($temp !== false)
3095
						{
3096
							$row['value'] = json_encode($temp);
3097
3098
							// Even though we have all values from the table, UPDATE is still faster than REPLACE
3099
							$smcFunc['db_query']('', '
3100
								UPDATE {db_prefix}themes
3101
								SET value = {string:prefs}
3102
								WHERE id_theme = {int:theme}
3103
									AND id_member = {int:member}
3104
									AND variable = {string:admin_prefs}',
3105
								array(
3106
									'prefs' => $row['value'],
3107
									'theme' => $row['id_theme'],
3108
									'member' => $row['id_member'],
3109
									'admin_prefs' => 'admin_preferences'
3110
								)
3111
							);
3112
3113
							if ($command_line)
3114
								echo ' done.';
3115
						}
3116
					}
3117
3118
					$smcFunc['db_free_result']($query);
3119
				}
3120
			}
3121
			else
3122
			{
3123
				// First item is always the key...
3124
				$key = $info[0];
3125
				unset($info[0]);
3126
3127
				// Now we know what columns we have and such...
3128
				if (count($info) == 2 && $info[2] === true)
3129
				{
3130
					$col_select = $info[1];
3131
					$where = ' WHERE ' . $info[1] . ' != {empty}';
3132
				}
3133
				else
3134
				{
3135
					$col_select = implode(', ', $info);
3136
				}
3137
3138
				$query = $smcFunc['db_query']('', '
3139
					SELECT ' . $key . ', ' . $col_select . '
3140
					FROM {db_prefix}' . $table . $where,
3141
					array()
3142
				);
3143
3144
				if ($smcFunc['db_num_rows']($query) != 0)
3145
				{
3146
					if ($command_line)
3147
					{
3148
						echo "\n" . ' +++ Fixing the "' . $table . '" table...';
3149
						flush();
3150
					}
3151
3152
					while ($row = $smcFunc['db_fetch_assoc']($query))
3153
					{
3154
						$update = '';
3155
3156
						// We already know what our key is...
3157
						foreach ($info as $col)
3158
						{
3159
							if ($col !== true && $row[$col] != '')
3160
							{
3161
								$temp = @safe_unserialize($row[$col]);
3162
3163
								if ($temp === false && $command_line)
3164
								{
3165
									echo "\nFailed to unserialize " . $row[$col] . "... Skipping\n";
3166
								}
3167
								else
3168
								{
3169
									$row[$col] = json_encode($temp);
3170
3171
									// Build our SET string and variables array
3172
									$update .= (empty($update) ? '' : ', ') . $col . ' = {string:' . $col . '}';
3173
									$vars[$col] = $row[$col];
3174
								}
3175
							}
3176
						}
3177
3178
						$vars[$key] = $row[$key];
3179
3180
						// In a few cases, we might have empty data, so don't try to update in those situations...
3181
						if (!empty($update))
3182
						{
3183
							$smcFunc['db_query']('', '
3184
								UPDATE {db_prefix}' . $table . '
3185
								SET ' . $update . '
3186
								WHERE ' . $key . ' = {' . ($key == 'session' ? 'string' : 'int') . ':' . $key . '}',
3187
								$vars
3188
							);
3189
						}
3190
					}
3191
3192
					if ($command_line)
3193
						echo ' done.';
3194
3195
					// Free up some memory...
3196
					$smcFunc['db_free_result']($query);
3197
				}
3198
			}
3199
			// If this is XML to keep it nice for the user do one table at a time anyway!
3200
			if (isset($_GET['xml']))
3201
				return upgradeExit();
3202
		}
3203
3204
		if ($command_line)
3205
		{
3206
			echo "\n" . 'Successful.' . "\n";
3207
			flush();
3208
		}
3209
		$upcontext['step_progress'] = 100;
3210
3211
		// Last but not least, insert a dummy setting so we don't have to do this again in the future...
3212
		updateSettings(array('json_done' => true));
3213
3214
		$_GET['substep'] = 0;
3215
		// Make sure we move on!
3216
		if ($command_line)
3217
			return DeleteUpgrade();
3218
3219
		return true;
3220
	}
3221
3222
	// If this fails we just move on to deleting the upgrade anyway...
3223
	$_GET['substep'] = 0;
3224
	return false;
3225
}
3226
3227
/******************************************************************************
3228
******************* Templates are below this point ****************************
3229
******************************************************************************/
3230
3231
// This is what is displayed if there's any chmod to be done. If not it returns nothing...
3232
function template_chmod()
3233
{
3234
	global $upcontext, $txt, $settings;
3235
3236
	// Don't call me twice!
3237
	if (!empty($upcontext['chmod_called']))
3238
		return;
3239
3240
	$upcontext['chmod_called'] = true;
3241
3242
	// Nothing?
3243
	if (empty($upcontext['chmod']['files']) && empty($upcontext['chmod']['ftp_error']))
3244
		return;
3245
3246
	// Was it a problem with Windows?
3247
	if (!empty($upcontext['chmod']['ftp_error']) && $upcontext['chmod']['ftp_error'] == 'total_mess')
3248
	{
3249
		echo '
3250
			<div class="error_message red">
3251
				The following files need to be writable to continue the upgrade. Please ensure the Windows permissions are correctly set to allow this:<br>
3252
				<ul style="margin: 2.5ex; font-family: monospace;">
3253
					<li>' . implode('</li>
3254
					<li>', $upcontext['chmod']['files']) . '</li>
3255
				</ul>
3256
			</div>';
3257
3258
		return false;
3259
	}
3260
3261
	echo '
3262
		<div class="panel">
3263
			<h2>Your FTP connection information</h2>
3264
			<h3>The upgrader can fix any issues with file permissions to make upgrading as simple as possible. Simply enter your connection information below or alternatively click <a href="#" onclick="warning_popup();">here</a> for a list of files which need to be changed.</h3>
3265
			<script>
3266
				function warning_popup()
3267
				{
3268
					popup = window.open(\'\',\'popup\',\'height=150,width=400,scrollbars=yes\');
3269
					var content = popup.document;
3270
					content.write(\'<!DOCTYPE html>\n\');
3271
					content.write(\'<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>\n\t<head>\n\t\t<meta name="robots" content="noindex">\n\t\t\');
3272
					content.write(\'<title>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\');
3273
					content.write(\'<div class="windowbg description">\n\t\t\t<h4>The following files needs to be made writable to continue:</h4>\n\t\t\t\');
3274
					content.write(\'<p>', implode('<br>\n\t\t\t', $upcontext['chmod']['files']), '</p>\n\t\t\t\');';
3275
3276
	if (isset($upcontext['systemos']) && $upcontext['systemos'] == 'linux')
3277
		echo '
3278
					content.write(\'<hr>\n\t\t\t\');
3279
					content.write(\'<p>If you have a shell account, the convenient below command can automatically correct permissions on these files</p>\n\t\t\t\');
3280
					content.write(\'<tt># chmod a+w ', implode(' ', $upcontext['chmod']['files']), '</tt>\n\t\t\t\');';
3281
3282
	echo '
3283
					content.write(\'<a href="javascript:self.close();">close</a>\n\t\t</div>\n\t</body>\n</html>\');
3284
					content.close();
3285
				}
3286
			</script>';
3287
3288
	if (!empty($upcontext['chmod']['ftp_error']))
3289
		echo '
3290
			<div class="error_message red">
3291
				The following error was encountered when trying to connect:<br><br>
3292
				<code>', $upcontext['chmod']['ftp_error'], '</code>
3293
			</div>
3294
			<br>';
3295
3296
	if (empty($upcontext['chmod_in_form']))
3297
		echo '
3298
	<form action="', $upcontext['form_url'], '" method="post">';
3299
3300
	echo '
3301
		<table width="520" border="0" align="center" style="margin-bottom: 1ex;">
3302
			<tr>
3303
				<td width="26%" valign="top" class="textbox"><label for="ftp_server">', $txt['ftp_server'], ':</label></td>
3304
				<td>
3305
					<div style="float: right; margin-right: 1px;"><label for="ftp_port" class="textbox"><strong>', $txt['ftp_port'], ':&nbsp;</strong></label> <input type="text" size="3" name="ftp_port" id="ftp_port" value="', isset($upcontext['chmod']['port']) ? $upcontext['chmod']['port'] : '21', '" class="input_text"></div>
3306
					<input type="text" size="30" name="ftp_server" id="ftp_server" value="', isset($upcontext['chmod']['server']) ? $upcontext['chmod']['server'] : 'localhost', '" style="width: 70%;" class="input_text">
3307
					<div class="smalltext block">', $txt['ftp_server_info'], '</div>
3308
				</td>
3309
			</tr><tr>
3310
				<td width="26%" valign="top" class="textbox"><label for="ftp_username">', $txt['ftp_username'], ':</label></td>
3311
				<td>
3312
					<input type="text" size="50" name="ftp_username" id="ftp_username" value="', isset($upcontext['chmod']['username']) ? $upcontext['chmod']['username'] : '', '" style="width: 99%;" class="input_text">
3313
					<div class="smalltext block">', $txt['ftp_username_info'], '</div>
3314
				</td>
3315
			</tr><tr>
3316
				<td width="26%" valign="top" class="textbox"><label for="ftp_password">', $txt['ftp_password'], ':</label></td>
3317
				<td>
3318
					<input type="password" size="50" name="ftp_password" id="ftp_password" style="width: 99%;" class="input_password">
3319
					<div class="smalltext block">', $txt['ftp_password_info'], '</div>
3320
				</td>
3321
			</tr><tr>
3322
				<td width="26%" valign="top" class="textbox"><label for="ftp_path">', $txt['ftp_path'], ':</label></td>
3323
				<td style="padding-bottom: 1ex;">
3324
					<input type="text" size="50" name="ftp_path" id="ftp_path" value="', isset($upcontext['chmod']['path']) ? $upcontext['chmod']['path'] : '', '" style="width: 99%;" class="input_text">
3325
					<div class="smalltext block">', !empty($upcontext['chmod']['path']) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'], '</div>
3326
				</td>
3327
			</tr>
3328
		</table>
3329
3330
		<div class="righttext" style="margin: 1ex;"><input type="submit" value="', $txt['ftp_connect'], '" class="button_submit"></div>
3331
	</div>';
3332
3333
	if (empty($upcontext['chmod_in_form']))
3334
		echo '
3335
	</form>';
3336
}
3337
3338
function template_upgrade_above()
3339
{
3340
	global $modSettings, $txt, $settings, $upcontext, $upgradeurl;
3341
3342
	echo '<!DOCTYPE html>
3343
<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>
3344
	<head>
3345
		<meta charset="', isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8', '">
3346
		<meta name="robots" content="noindex">
3347
		<title>', $txt['upgrade_upgrade_utility'], '</title>
3348
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/index.css?alp21">
3349
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/install.css?alp21">
3350
		', $txt['lang_rtl'] == true ? '<link rel="stylesheet" href="' . $settings['default_theme_url'] . '/css/rtl.css?alp21">' : '', '
3351
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
3352
		<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
3353
		<script>
3354
			var smf_scripturl = \'', $upgradeurl, '\';
3355
			var smf_charset = \'', (empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'UTF-8' : $txt['lang_character_set']) : $modSettings['global_character_set']), '\';
3356
			var startPercent = ', $upcontext['overall_percent'], ';
3357
3358
			// This function dynamically updates the step progress bar - and overall one as required.
3359
			function updateStepProgress(current, max, overall_weight)
3360
			{
3361
				// What out the actual percent.
3362
				var width = parseInt((current / max) * 100);
3363
				if (document.getElementById(\'step_progress\'))
3364
				{
3365
					document.getElementById(\'step_progress\').style.width = width + "%";
3366
					setInnerHTML(document.getElementById(\'step_text\'), width + "%");
3367
				}
3368
				if (overall_weight && document.getElementById(\'overall_progress\'))
3369
				{
3370
					overall_width = parseInt(startPercent + width * (overall_weight / 100));
3371
					document.getElementById(\'overall_progress\').style.width = overall_width + "%";
3372
					setInnerHTML(document.getElementById(\'overall_text\'), overall_width + "%");
3373
				}
3374
			}
3375
		</script>
3376
	</head>
3377
	<body>
3378
	<div id="footerfix">
3379
		<div id="header">
3380
			<h1 class="forumtitle">', $txt['upgrade_upgrade_utility'], '</h1>
3381
			<img id="smflogo" src="', $settings['default_theme_url'], '/images/smflogo.png" alt="Simple Machines Forum" title="Simple Machines Forum">
3382
		</div>
3383
	<div id="wrapper">
3384
		<div id="upper_section">
3385
			<div id="inner_section">
3386
				<div id="inner_wrap">
3387
				</div>
3388
			</div>
3389
		</div>
3390
		<div id="content_section">
3391
		<div id="main_content_section">
3392
			<div id="main_steps">
3393
				<h2>', $txt['upgrade_progress'], '</h2>
3394
				<ul>';
3395
3396 View Code Duplication
	foreach ($upcontext['steps'] as $num => $step)
3397
		echo '
3398
						<li class="', $num < $upcontext['current_step'] ? 'stepdone' : ($num == $upcontext['current_step'] ? 'stepcurrent' : 'stepwaiting'), '">', $txt['upgrade_step'], ' ', $step[0], ': ', $step[1], '</li>';
3399
3400
	echo '
3401
					</ul>
3402
			</div>
3403
3404
			<div id="progress_bar">
3405
				<div id="overall_text">', $upcontext['overall_percent'], '%</div>
3406
				<div id="overall_progress" style="width: ', $upcontext['overall_percent'], '%;">
3407
					<span>', $txt['upgrade_overall_progress'], '</span>
3408
				</div>
3409
			</div>';
3410
3411
	if (isset($upcontext['step_progress']))
3412
		echo '
3413
				<br>
3414
				<br>
3415
				<div id="progress_bar_step">
3416
					<div id="step_text">', $upcontext['step_progress'], '%</div>
3417
					<div id="step_progress" style="width: ', $upcontext['step_progress'], '%;background-color: #ffd000;">
3418
						<span>', $txt['upgrade_step_progress'], '</span>
3419
					</div>
3420
				</div>';
3421
3422
	echo '
3423
				<div id="substep_bar_div" class="smalltext" style="float: left;width: 50%;margin-top: 0.6em;display: ', isset($upcontext['substep_progress']) ? '' : 'none', ';">', isset($upcontext['substep_progress_name']) ? trim(strtr($upcontext['substep_progress_name'], array('.' => ''))) : '', ':</div>
3424
				<div id="substep_bar_div2" style="float: left;font-size: 8pt; height: 12pt; border: 1px solid black; background-color: white; width: 33%; margin: 0.6em auto 0 6em; display: ', isset($upcontext['substep_progress']) ? '' : 'none', ';">
3425
					<div id="substep_text" style="color: #000; position: absolute; margin-left: -5em;">', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : '', '%</div>
3426
					<div id="substep_progress" style="width: ', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%; height: 12pt; z-index: 1; background-color: #eebaf4;">&nbsp;</div>
3427
				</div>';
3428
3429
	// How long have we been running this?
3430
	$elapsed = time() - $upcontext['started'];
3431
	$mins = (int) ($elapsed / 60);
3432
	$seconds = $elapsed - $mins * 60;
3433
	echo '
3434
								<br> <br> <br> <br> <br>
3435
								<div class="smalltext" style="padding: 5px; text-align: center;"><br>', $txt['upgrade_time_elapsed'], ':
3436
									<span id="mins_elapsed">', $mins, '</span> ', $txt['upgrade_time_mins'], ', <span id="secs_elapsed">', $seconds, '</span> ', $txt['upgrade_time_secs'], '.
3437
								</div>';
3438
	echo '
3439
			</div>
3440
			</div>
3441
			<div id="main_screen" class="clear">
3442
				<h2>', $upcontext['page_title'], '</h2>
3443
				<div class="panel">
3444
					<div style="max-height: 360px; overflow: auto;">';
3445
}
3446
3447
function template_upgrade_below()
3448
{
3449
	global $upcontext, $txt;
3450
3451
	if (!empty($upcontext['pause']))
3452
		echo '
3453
								<em>', $txt['upgrade_incomplete'], '.</em><br>
3454
3455
								<h2 style="margin-top: 2ex;">', $txt['upgrade_not_quite_done'], '</h2>
3456
								<h3>
3457
									', $txt['upgrade_paused_overload'], '
3458
								</h3>';
3459
3460
	if (!empty($upcontext['custom_warning']))
3461
		echo '
3462
								<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3463
									<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3464
									<strong style="text-decoration: underline;">', $txt['upgrade_note'], '</strong><br>
3465
									<div style="padding-left: 6ex;">', $upcontext['custom_warning'], '</div>
3466
								</div>';
3467
3468
	echo '
3469
								<div class="righttext" style="margin: 1ex;">';
3470
3471
	if (!empty($upcontext['continue']))
3472
		echo '
3473
									<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '"', $upcontext['continue'] == 2 ? ' disabled' : '', ' class="button_submit">';
3474
	if (!empty($upcontext['skip']))
3475
		echo '
3476
									<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="dontSubmit = true; document.getElementById(\'contbutt\').disabled = \'disabled\'; return true;" class="button_submit">';
3477
3478
	echo '
3479
								</div>
3480
							</form>
3481
						</div>
3482
				</div>
3483
			</div>
3484
			</div>
3485
		</div>
3486
		<div id="footer">
3487
			<ul>
3488
				<li class="copyright"><a href="http://www.simplemachines.org/" title="Simple Machines Forum" target="_blank" class="new_win">SMF &copy; 2017, Simple Machines</a></li>
3489
			</ul>
3490
		</div>
3491
	</body>
3492
</html>';
3493
3494
	// Are we on a pause?
3495
	if (!empty($upcontext['pause']))
3496
	{
3497
		echo '
3498
		<script>
3499
			window.onload = doAutoSubmit;
3500
			var countdown = 3;
3501
			var dontSubmit = false;
3502
3503
			function doAutoSubmit()
3504
			{
3505
				if (countdown == 0 && !dontSubmit)
3506
					document.upform.submit();
3507
				else if (countdown == -1)
3508
					return;
3509
3510
				document.getElementById(\'contbutt\').value = "', $txt['upgrade_continue'], ' (" + countdown + ")";
3511
				countdown--;
3512
3513
				setTimeout("doAutoSubmit();", 1000);
3514
			}
3515
		</script>';
3516
	}
3517
}
3518
3519
function template_xml_above()
3520
{
3521
	global $upcontext;
3522
3523
	echo '<', '?xml version="1.0" encoding="UTF-8"?', '>
3524
	<smf>';
3525
3526
	if (!empty($upcontext['get_data']))
3527
		foreach ($upcontext['get_data'] as $k => $v)
3528
			echo '
3529
		<get key="', $k, '">', $v, '</get>';
3530
}
3531
3532
function template_xml_below()
3533
{
3534
	echo '
3535
		</smf>';
3536
}
3537
3538
function template_error_message()
3539
{
3540
	global $upcontext;
3541
3542
	echo '
3543
	<div class="error_message red">
3544
		', $upcontext['error_msg'], '
3545
		<br>
3546
		<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 3546

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...
3547
	</div>';
3548
}
3549
3550
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 (L1917-1966) 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...
3551
{
3552
	global $upcontext, $disable_security, $settings, $txt;
3553
3554
	echo '
3555
		<script src="http://www.simplemachines.org/smf/current-version.js?version=' . SMF_VERSION . '"></script>
3556
			<h3>', sprintf($txt['upgrade_ready_proceed'], SMF_VERSION), '</h3>
3557
	<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">
3558
		<input type="hidden" name="', $upcontext['login_token_var'], '" value="', $upcontext['login_token'], '">
3559
		<div id="version_warning" style="margin: 2ex; padding: 2ex; border: 2px dashed #a92174; color: black; background-color: #fbbbe2; display: none;">
3560
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3561
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3562
			<div style="padding-left: 6ex;">
3563
				', sprintf($txt['upgrade_warning_out_of_date'], SMF_VERSION), '
3564
			</div>
3565
		</div>';
3566
3567
	$upcontext['chmod_in_form'] = true;
3568
	template_chmod();
3569
3570
	// For large, pre 1.1 RC2 forums give them a warning about the possible impact of this upgrade!
3571
	if ($upcontext['is_large_forum'])
3572
		echo '
3573
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3574
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3575
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3576
			<div style="padding-left: 6ex;">
3577
				', $txt['upgrade_warning_lots_data'], '
3578
			</div>
3579
		</div>';
3580
3581
	// A warning message?
3582
	if (!empty($upcontext['warning']))
3583
		echo '
3584
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3585
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3586
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3587
			<div style="padding-left: 6ex;">
3588
				', $upcontext['warning'], '
3589
			</div>
3590
		</div>';
3591
3592
	// Paths are incorrect?
3593
	echo '
3594
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #804840; color: black; background-color: #fe5a44; ', (file_exists($settings['default_theme_dir'] . '/scripts/script.js') ? 'display: none;' : ''), '" id="js_script_missing_error">
3595
			<div style="float: left; width: 2ex; font-size: 2em; color: black;">!!</div>
3596
			<strong style="text-decoration: underline;">', $txt['upgrade_critical_error'], '</strong><br>
3597
			<div style="padding-left: 6ex;">
3598
				', $txt['upgrade_error_script_js'], '
3599
			</div>
3600
		</div>';
3601
3602
	// Is there someone already doing this?
3603
	if (!empty($upcontext['user']['id']) && (time() - $upcontext['started'] < 72600 || time() - $upcontext['updated'] < 3600))
3604
	{
3605
		$ago = time() - $upcontext['started'];
3606
		if ($ago < 60)
3607
			$ago = $ago . ' seconds';
3608
		elseif ($ago < 3600)
3609
			$ago = (int) ($ago / 60) . ' minutes';
3610
		else
3611
			$ago = (int) ($ago / 3600) . ' hours';
3612
3613
		$active = time() - $upcontext['updated'];
3614
		if ($active < 60)
3615
			$updated = $active . ' seconds';
3616
		elseif ($active < 3600)
3617
			$updated = (int) ($active / 60) . ' minutes';
3618
		else
3619
			$updated = (int) ($active / 3600) . ' hours';
3620
3621
		echo '
3622
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3623
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3624
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3625
			<div style="padding-left: 6ex;">
3626
				&quot;', $upcontext['user']['name'], '&quot; has been running the upgrade script for the last ', $ago, ' - and was last active ', $updated, ' ago.';
3627
3628
		if ($active < 600)
3629
			echo '
3630
				We recommend that you do not run this script unless you are sure that ', $upcontext['user']['name'], ' has completed their upgrade.';
3631
3632
		if ($active > $upcontext['inactive_timeout'])
3633
			echo '
3634
				<br><br>You can choose to either run the upgrade again from the beginning - or alternatively continue from the last step reached during the last upgrade.';
3635
		else
3636
			echo '
3637
				<br><br>This upgrade script cannot be run until ', $upcontext['user']['name'], ' has been inactive for at least ', ($upcontext['inactive_timeout'] > 120 ? round($upcontext['inactive_timeout'] / 60, 1) . ' minutes!' : $upcontext['inactive_timeout'] . ' seconds!');
3638
3639
		echo '
3640
			</div>
3641
		</div>';
3642
	}
3643
3644
	echo '
3645
			<strong>Admin Login: ', $disable_security ? '(DISABLED)' : '', '</strong>
3646
			<h3>For security purposes please login with your admin account to proceed with the upgrade.</h3>
3647
			<table>
3648
				<tr valign="top">
3649
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Username:</strong></td>
3650
					<td>
3651
						<input type="text" name="user" value="', !empty($upcontext['username']) ? $upcontext['username'] : '', '"', $disable_security ? ' disabled' : '', ' class="input_text">';
3652
3653
	if (!empty($upcontext['username_incorrect']))
3654
		echo '
3655
						<div class="smalltext" style="color: red;">Username Incorrect</div>';
3656
3657
	echo '
3658
					</td>
3659
				</tr>
3660
				<tr valign="top">
3661
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Password:</strong></td>
3662
					<td>
3663
						<input type="password" name="passwrd" value=""', $disable_security ? ' disabled' : '', ' class="input_password">
3664
						<input type="hidden" name="hash_passwrd" value="">';
3665
3666
	if (!empty($upcontext['password_failed']))
3667
		echo '
3668
						<div class="smalltext" style="color: red;">Password Incorrect</div>';
3669
3670
	echo '
3671
					</td>
3672
				</tr>';
3673
3674
	// Can they continue?
3675
	if (!empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] >= $upcontext['inactive_timeout'] && $upcontext['user']['step'] > 1)
3676
	{
3677
		echo '
3678
				<tr>
3679
					<td colspan="2">
3680
						<label for="cont"><input type="checkbox" id="cont" name="cont" checked class="input_check">Continue from step reached during last execution of upgrade script.</label>
3681
					</td>
3682
				</tr>';
3683
	}
3684
3685
	echo '
3686
			</table><br>
3687
			<span class="smalltext">
3688
				<strong>Note:</strong> If necessary the above security check can be bypassed for users who may administrate a server but not have admin rights on the forum. In order to bypass the above check simply open &quot;upgrade.php&quot; in a text editor and replace &quot;$disable_security = false;&quot; with &quot;$disable_security = true;&quot; and refresh this page.
3689
			</span>
3690
			<input type="hidden" name="login_attempt" id="login_attempt" value="1">
3691
			<input type="hidden" name="js_works" id="js_works" value="0">';
3692
3693
	// Say we want the continue button!
3694
	$upcontext['continue'] = !empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] < $upcontext['inactive_timeout'] ? 2 : 1;
3695
3696
	// This defines whether javascript is going to work elsewhere :D
3697
	echo '
3698
		<script>
3699
			if (\'XMLHttpRequest\' in window && document.getElementById(\'js_works\'))
3700
				document.getElementById(\'js_works\').value = 1;
3701
3702
			// Latest version?
3703
			function smfCurrentVersion()
3704
			{
3705
				var smfVer, yourVer;
3706
3707
				if (!(\'smfVersion\' in window))
3708
					return;
3709
3710
				window.smfVersion = window.smfVersion.replace(/SMF\s?/g, \'\');
3711
3712
				smfVer = document.getElementById(\'smfVersion\');
3713
				yourVer = document.getElementById(\'yourVersion\');
3714
3715
				setInnerHTML(smfVer, window.smfVersion);
3716
3717
				var currentVersion = getInnerHTML(yourVer);
3718
				if (currentVersion < window.smfVersion)
3719
					document.getElementById(\'version_warning\').style.display = \'\';
3720
			}
3721
			addLoadEvent(smfCurrentVersion);
3722
3723
			// This checks that the script file even exists!
3724
			if (typeof(smfSelectText) == \'undefined\')
3725
				document.getElementById(\'js_script_missing_error\').style.display = \'\';
3726
3727
		</script>';
3728
}
3729
3730
function template_upgrade_options()
3731
{
3732
	global $upcontext, $modSettings, $db_prefix, $mmessage, $mtitle, $db_type;
3733
3734
	echo '
3735
			<h3>Before the upgrade gets underway please review the options below - and hit continue when you\'re ready to begin.</h3>
3736
			<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">';
3737
3738
	// Warning message?
3739
	if (!empty($upcontext['upgrade_options_warning']))
3740
		echo '
3741
		<div style="margin: 1ex; padding: 1ex; border: 1px dashed #cc3344; color: black; background-color: #ffe4e9;">
3742
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3743
			<strong style="text-decoration: underline;">Warning!</strong><br>
3744
			<div style="padding-left: 4ex;">
3745
				', $upcontext['upgrade_options_warning'], '
3746
			</div>
3747
		</div>';
3748
3749
	echo '
3750
				<table>
3751
					<tr valign="top">
3752
						<td width="2%">
3753
							<input type="checkbox" name="backup" id="backup" value="1" class="input_check">
3754
						</td>
3755
						<td width="100%">
3756
							<label for="backup">Backup tables in your database with the prefix &quot;backup_' . $db_prefix . '&quot;.</label> (recommended!)
3757
						</td>
3758
					</tr>
3759
					<tr valign="top">
3760
						<td width="2%">
3761
							<input type="checkbox" name="maint" id="maint" value="1" checked class="input_check">
3762
						</td>
3763
						<td width="100%">
3764
							<label for="maint">Put the forum into maintenance mode during upgrade.</label> <span class="smalltext">(<a href="#" onclick="document.getElementById(\'mainmess\').style.display = document.getElementById(\'mainmess\').style.display == \'\' ? \'none\' : \'\'">Customize</a>)</span>
3765
							<div id="mainmess" style="display: none;">
3766
								<strong class="smalltext">Maintenance Title: </strong><br>
3767
								<input type="text" name="maintitle" size="30" value="', htmlspecialchars($mtitle), '" class="input_text"><br>
3768
								<strong class="smalltext">Maintenance Message: </strong><br>
3769
								<textarea name="mainmessage" rows="3" cols="50">', htmlspecialchars($mmessage), '</textarea>
3770
							</div>
3771
						</td>
3772
					</tr>
3773
					<tr valign="top">
3774
						<td width="2%">
3775
							<input type="checkbox" name="debug" id="debug" value="1" class="input_check">
3776
						</td>
3777
						<td width="100%">
3778
							<label for="debug">Output extra debugging information</label>
3779
						</td>
3780
					</tr>
3781
					<tr valign="top">
3782
						<td width="2%">
3783
							<input type="checkbox" name="empty_error" id="empty_error" value="1" class="input_check">
3784
						</td>
3785
						<td width="100%">
3786
							<label for="empty_error">Empty error log before upgrading</label>
3787
						</td>
3788
					</tr>';
3789
3790
	if (!empty($upcontext['karma_installed']['good']) || !empty($upcontext['karma_installed']['bad']))
3791
		echo '
3792
					<tr valign="top">
3793
						<td width="2%">
3794
							<input type="checkbox" name="delete_karma" id="delete_karma" value="1" class="input_check">
3795
						</td>
3796
						<td width="100%">
3797
							<label for="delete_karma">Delete all karma settings and info from the DB</label>
3798
						</td>
3799
					</tr>';
3800
3801
	echo '
3802
					<tr valign="top">
3803
						<td width="2%">
3804
							<input type="checkbox" name="stat" id="stat" value="1"', empty($modSettings['allow_sm_stats']) ? '' : ' checked', ' class="input_check">
3805
						</td>
3806
						<td width="100%">
3807
							<label for="stat">
3808
								Allow Simple Machines to Collect Basic Stats Monthly.<br>
3809
								<span class="smalltext">If enabled, this will allow Simple Machines to visit your site once a month to collect basic statistics. This will help us make decisions as to which configurations to optimise the software for. For more information please visit our <a href="http://www.simplemachines.org/about/stats.php" target="_blank">info page</a>.</span>
3810
							</label>
3811
						</td>
3812
					</tr>
3813
				</table>
3814
				<input type="hidden" name="upcont" value="1">';
3815
3816
	// We need a normal continue button here!
3817
	$upcontext['continue'] = 1;
3818
}
3819
3820
// Template for the database backup tool/
3821
function template_backup_database()
3822
{
3823
	global $upcontext, $support_js, $is_debug;
3824
3825
	echo '
3826
			<h3>Please wait while a backup is created. For large forums this may take some time!</h3>';
3827
3828
	echo '
3829
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
3830
			<input type="hidden" name="backup_done" id="backup_done" value="0">
3831
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
3832
			<div id="debug_section" style="height: ', ($is_debug ? '195' : '12') , 'px; overflow: auto;">
3833
			<span id="debuginfo"></span>
3834
			</div>';
3835
3836
	// Dont any tables so far?
3837
	if (!empty($upcontext['previous_tables']))
3838
		foreach ($upcontext['previous_tables'] as $table)
3839
			echo '
3840
			<br>Completed Table: &quot;', $table, '&quot;.';
3841
3842
	echo '
3843
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
3844
			<br><span id="commess" style="font-weight: bold; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">Backup Complete! Click Continue to Proceed.</span>';
3845
3846
	// Continue please!
3847
	$upcontext['continue'] = $support_js ? 2 : 1;
3848
3849
	// If javascript allows we want to do this using XML.
3850
	if ($support_js)
3851
	{
3852
		echo '
3853
		<script>
3854
			var lastTable = ', $upcontext['cur_table_num'], ';
3855
			function getNextTables()
3856
			{
3857
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
3858
			}
3859
3860
			// Got an update!
3861
			function onBackupUpdate(oXMLDoc)
3862
			{
3863
				var sCurrentTableName = "";
3864
				var iTableNum = 0;
3865
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
3866
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
3867
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
3868
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
3869
3870
				// Update the page.
3871
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
3872
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
3873
				lastTable = iTableNum;
3874
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
3875
3876
		// If debug flood the screen.
3877
		if ($is_debug)
3878
			echo '
3879
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
3880
3881
				if (document.getElementById(\'debug_section\').scrollHeight)
3882
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
3883
3884
		echo '
3885
				// Get the next update...
3886
				if (iTableNum == ', $upcontext['table_count'], ')
3887
				{
3888
					document.getElementById(\'commess\').style.display = "";
3889
					document.getElementById(\'current_tab_div\').style.display = "none";
3890
					document.getElementById(\'contbutt\').disabled = 0;
3891
					document.getElementById(\'backup_done\').value = 1;
3892
				}
3893
				else
3894
					getNextTables();
3895
			}
3896
			getNextTables();
3897
		//# sourceURL=dynamicScript-bkup.js 
3898
		</script>';
3899
	}
3900
}
3901
3902
function template_backup_xml()
3903
{
3904
	global $upcontext;
3905
3906
	echo '
3907
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
3908
}
3909
3910
// Here is the actual "make the changes" template!
3911
function template_database_changes()
3912
{
3913
	global $upcontext, $support_js, $is_debug, $timeLimitThreshold;
3914
3915
	if (empty($is_debug) && !empty($upcontext['upgrade_status']['debug']))
3916
		$is_debug = true;
3917
3918
	echo '
3919
		<h3>Executing database changes</h3>
3920
		<h4 style="font-style: italic;">Please be patient - this may take some time on large forums. The time elapsed increments from the server to show progress is being made!</h4>';
3921
3922
	echo '
3923
		<form action="', $upcontext['form_url'], '&amp;filecount=', $upcontext['file_count'], '" name="upform" id="upform" method="post">
3924
		<input type="hidden" name="database_done" id="database_done" value="0">';
3925
3926
	// No javascript looks rubbish!
3927
	if (!$support_js)
3928
	{
3929
		foreach ($upcontext['actioned_items'] as $num => $item)
3930
		{
3931
			if ($num != 0)
3932
				echo ' Successful!';
3933
			echo '<br>' . $item;
3934
		}
3935 View Code Duplication
		if (!empty($upcontext['changes_complete']))
3936
		{
3937
			if ($is_debug)
3938
			{
3939
				$active = time() - $upcontext['started'];
3940
				$hours = floor($active / 3600);
3941
				$minutes = intval(($active / 60) % 60);
3942
				$seconds = intval($active % 60);
3943
3944
				$totalTime = '';
3945
				if ($hours > 0)
3946
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
3947
				if ($minutes > 0)
3948
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
3949
				if ($seconds > 0)
3950
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
3951
			}
3952
3953
			if ($is_debug && !empty($totalTime))
3954
				echo ' Successful! Completed in ', $totalTime, '<br><br>';
3955
			else
3956
				echo ' Successful!<br><br>';
3957
3958
			echo '<span id="commess" style="font-weight: bold;">1 Database Updates Complete! Click Continue to Proceed.</span><br>';
3959
		}
3960
	}
3961
	else
3962
	{
3963
		// Tell them how many files we have in total.
3964
		if ($upcontext['file_count'] > 1)
3965
			echo '
3966
		<strong id="info1">Executing upgrade script <span id="file_done">', $upcontext['cur_file_num'], '</span> of ', $upcontext['file_count'], '.</strong>';
3967
3968
		echo '
3969
		<h3 id="info2"><strong>Executing:</strong> &quot;<span id="cur_item_name">', $upcontext['current_item_name'], '</span>&quot; (<span id="item_num">', $upcontext['current_item_num'], '</span> of <span id="total_items"><span id="item_count">', $upcontext['total_items'], '</span>', $upcontext['file_count'] > 1 ? ' - of this script' : '', ')</span></h3>
3970
		<br><span id="commess" style="font-weight: bold; display: ', !empty($upcontext['changes_complete']) || $upcontext['current_debug_item_num'] == $upcontext['debug_items'] ? 'inline' : 'none', ';">Database Updates Complete! Click Continue to Proceed.</span>';
3971
3972 View Code Duplication
		if ($is_debug)
3973
		{
3974
			if ($upcontext['current_debug_item_num'] == $upcontext['debug_items'])
3975
			{
3976
				$active = time() - $upcontext['started'];
3977
				$hours = floor($active / 3600);
3978
				$minutes = intval(($active / 60) % 60);
3979
				$seconds = intval($active % 60);
3980
3981
				$totalTime = '';
3982
				if ($hours > 0)
3983
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
3984
				if ($minutes > 0)
3985
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
3986
				if ($seconds > 0)
3987
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
3988
			}
3989
3990
			echo '
3991
			<br><span id="upgradeCompleted">';
3992
3993
			if (!empty($totalTime))
3994
				echo 'Completed in ', $totalTime, '<br>';
3995
3996
			echo '</span>
3997
			<div id="debug_section" style="height: 117px; overflow: auto;">
3998
			<span id="debuginfo"></span>
3999
			</div>';
4000
		}
4001
	}
4002
4003
	// Place for the XML error message.
4004
	echo '
4005
		<div id="error_block" style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9; display: ', empty($upcontext['error_message']) ? 'none' : '', ';">
4006
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4007
			<strong style="text-decoration: underline;">Error!</strong><br>
4008
			<div style="padding-left: 6ex;" id="error_message">', isset($upcontext['error_message']) ? $upcontext['error_message'] : 'Unknown Error!', '</div>
4009
		</div>';
4010
4011
	// We want to continue at some point!
4012
	$upcontext['continue'] = $support_js ? 2 : 1;
4013
4014
	// If javascript allows we want to do this using XML.
4015
	if ($support_js)
4016
	{
4017
		echo '
4018
		<script>
4019
			var lastItem = ', $upcontext['current_debug_item_num'], ';
4020
			var sLastString = "', strtr($upcontext['current_debug_item_name'], array('"' => '&quot;')), '";
4021
			var iLastSubStepProgress = -1;
4022
			var curFile = ', $upcontext['cur_file_num'], ';
4023
			var totalItems = 0;
4024
			var prevFile = 0;
4025
			var retryCount = 0;
4026
			var testvar = 0;
4027
			var timeOutID = 0;
4028
			var getData = "";
4029
			var debugItems = ', $upcontext['debug_items'], ';';
4030
4031
		if ($is_debug)
4032
			echo '
4033
			var upgradeStartTime = ' . $upcontext['started'] . ';';
4034
4035
		echo '
4036
			function getNextItem()
4037
			{
4038
				// We want to track this...
4039
				if (timeOutID)
4040
					clearTimeout(timeOutID);
4041
				timeOutID = window.setTimeout("retTimeout()", ', $timeLimitThreshold, '000);
4042
4043
				getXMLDocument(\'', $upcontext['form_url'], '&xml&filecount=', $upcontext['file_count'], '&substep=\' + lastItem + getData, onItemUpdate);
4044
			}
4045
4046
			// Got an update!
4047
			function onItemUpdate(oXMLDoc)
4048
			{
4049
				var sItemName = "";
4050
				var sDebugName = "";
4051
				var iItemNum = 0;
4052
				var iSubStepProgress = -1;
4053
				var iDebugNum = 0;
4054
				var bIsComplete = 0;
4055
				getData = "";
4056
4057
				// We\'ve got something - so reset the timeout!
4058
				if (timeOutID)
4059
					clearTimeout(timeOutID);
4060
4061
				// Assume no error at this time...
4062
				document.getElementById("error_block").style.display = "none";
4063
4064
				// Are we getting some duff info?
4065
				if (!oXMLDoc.getElementsByTagName("item")[0])
4066
				{
4067
					// Too many errors?
4068
					if (retryCount > 15)
4069
					{
4070
						document.getElementById("error_block").style.display = "";
4071
						setInnerHTML(document.getElementById("error_message"), "Error retrieving information on step: " + (sDebugName == "" ? sLastString : sDebugName));';
4072
4073
	if ($is_debug)
4074
		echo '
4075
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4076
4077
	echo '
4078
					}
4079
					else
4080
					{
4081
						retryCount++;
4082
						getNextItem();
4083
					}
4084
					return false;
4085
				}
4086
4087
				// Never allow loops.
4088
				if (curFile == prevFile)
4089
				{
4090
					retryCount++;
4091
					if (retryCount > 10)
4092
					{
4093
						document.getElementById("error_block").style.display = "";
4094
						setInnerHTML(document.getElementById("error_message"), "Upgrade script appears to be going into a loop - step: " + sDebugName);';
4095
4096
	if ($is_debug)
4097
		echo '
4098
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4099
4100
	echo '
4101
					}
4102
				}
4103
				retryCount = 0;
4104
4105
				for (var i = 0; i < oXMLDoc.getElementsByTagName("item")[0].childNodes.length; i++)
4106
					sItemName += oXMLDoc.getElementsByTagName("item")[0].childNodes[i].nodeValue;
4107
				for (var i = 0; i < oXMLDoc.getElementsByTagName("debug")[0].childNodes.length; i++)
4108
					sDebugName += oXMLDoc.getElementsByTagName("debug")[0].childNodes[i].nodeValue;
4109
				for (var i = 0; i < oXMLDoc.getElementsByTagName("get").length; i++)
4110
				{
4111
					getData += "&" + oXMLDoc.getElementsByTagName("get")[i].getAttribute("key") + "=";
4112
					for (var j = 0; j < oXMLDoc.getElementsByTagName("get")[i].childNodes.length; j++)
4113
					{
4114
						getData += oXMLDoc.getElementsByTagName("get")[i].childNodes[j].nodeValue;
4115
					}
4116
				}
4117
4118
				iItemNum = oXMLDoc.getElementsByTagName("item")[0].getAttribute("num");
4119
				iDebugNum = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("num"));
4120
				bIsComplete = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("complete"));
4121
				iSubStepProgress = parseFloat(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("percent"));
4122
				sLastString = sDebugName + " (Item: " + iDebugNum + ")";
4123
4124
				curFile = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("num"));
4125
				debugItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("debug_items"));
4126
				totalItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("items"));
4127
4128
				// If we have an error we haven\'t completed!
4129
				if (oXMLDoc.getElementsByTagName("error")[0] && bIsComplete)
4130
					iDebugNum = lastItem;
4131
4132
				// Do we have the additional progress bar?
4133
				if (iSubStepProgress != -1)
4134
				{
4135
					document.getElementById("substep_bar_div").style.display = "";
4136
					document.getElementById("substep_bar_div2").style.display = "";
4137
					document.getElementById("substep_progress").style.width = iSubStepProgress + "%";
4138
					setInnerHTML(document.getElementById("substep_text"), iSubStepProgress + "%");
4139
					setInnerHTML(document.getElementById("substep_bar_div"), sDebugName.replace(/\./g, "") + ":");
4140
				}
4141
				else
4142
				{
4143
					document.getElementById("substep_bar_div").style.display = "none";
4144
					document.getElementById("substep_bar_div2").style.display = "none";
4145
				}
4146
4147
				// Move onto the next item?
4148
				if (bIsComplete)
4149
					lastItem = iDebugNum;
4150
				else
4151
					lastItem = iDebugNum - 1;
4152
4153
				// Are we finished?
4154
				if (bIsComplete && iDebugNum == -1 && curFile >= ', $upcontext['file_count'], ')
4155
				{';
4156
4157
		if ($is_debug)
4158
			echo '
4159
					document.getElementById(\'debug_section\').style.display = "none";
4160
4161
					var upgradeFinishedTime = parseInt(oXMLDoc.getElementsByTagName("curtime")[0].childNodes[0].nodeValue);
4162
					var diffTime = upgradeFinishedTime - upgradeStartTime;
4163
					var diffHours = Math.floor(diffTime / 3600);
4164
					var diffMinutes = parseInt((diffTime / 60) % 60);
4165
					var diffSeconds = parseInt(diffTime % 60);
4166
4167
					var totalTime = "";
4168
					if (diffHours > 0)
4169
						totalTime = totalTime + diffHours + " hour" + (diffHours > 1 ? "s" : "") + " ";
4170
					if (diffMinutes > 0)
4171
						totalTime = totalTime + diffMinutes + " minute" + (diffMinutes > 1 ? "s" : "") + " ";
4172
					if (diffSeconds > 0)
4173
						totalTime = totalTime + diffSeconds + " second" + (diffSeconds > 1 ? "s" : "");
4174
4175
					setInnerHTML(document.getElementById("upgradeCompleted"), "Completed in " + totalTime);';
4176
4177
		echo '
4178
4179
					document.getElementById(\'commess\').style.display = "";
4180
					document.getElementById(\'contbutt\').disabled = 0;
4181
					document.getElementById(\'database_done\').value = 1;';
4182
4183
		if ($upcontext['file_count'] > 1)
4184
			echo '
4185
					document.getElementById(\'info1\').style.display = "none";';
4186
4187
		echo '
4188
					document.getElementById(\'info2\').style.display = "none";
4189
					updateStepProgress(100, 100, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4190
					return true;
4191
				}
4192
				// Was it the last step in the file?
4193
				else if (bIsComplete && iDebugNum == -1)
4194
				{
4195
					lastItem = 0;
4196
					prevFile = curFile;';
4197
4198
		if ($is_debug)
4199
			echo '
4200
					setOuterHTML(document.getElementById(\'debuginfo\'), \'Moving to next script file...done<br><span id="debuginfo"><\' + \'/span>\');';
4201
4202
		echo '
4203
					getNextItem();
4204
					return true;
4205
				}';
4206
4207
		// If debug scroll the screen.
4208
		if ($is_debug)
4209
			echo '
4210
				if (iLastSubStepProgress == -1)
4211
				{
4212
					// Give it consistent dots.
4213
					dots = sDebugName.match(/\./g);
4214
					numDots = dots ? dots.length : 0;
4215
					for (var i = numDots; i < 3; i++)
4216
						sDebugName += ".";
4217
					setOuterHTML(document.getElementById(\'debuginfo\'), sDebugName + \'<span id="debuginfo"><\' + \'/span>\');
4218
				}
4219
				iLastSubStepProgress = iSubStepProgress;
4220
4221
				if (bIsComplete)
4222
					setOuterHTML(document.getElementById(\'debuginfo\'), \'done<br><span id="debuginfo"><\' + \'/span>\');
4223
				else
4224
					setOuterHTML(document.getElementById(\'debuginfo\'), \'...<span id="debuginfo"><\' + \'/span>\');
4225
4226
				if (document.getElementById(\'debug_section\').scrollHeight)
4227
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4228
4229
		echo '
4230
				// Update the page.
4231
				setInnerHTML(document.getElementById(\'item_num\'), iItemNum);
4232
				setInnerHTML(document.getElementById(\'cur_item_name\'), sItemName);';
4233
4234
		if ($upcontext['file_count'] > 1)
4235
		{
4236
			echo '
4237
				setInnerHTML(document.getElementById(\'file_done\'), curFile);
4238
				setInnerHTML(document.getElementById(\'item_count\'), totalItems);';
4239
		}
4240
4241
		echo '
4242
				// Is there an error?
4243
				if (oXMLDoc.getElementsByTagName("error")[0])
4244
				{
4245
					var sErrorMsg = "";
4246
					for (var i = 0; i < oXMLDoc.getElementsByTagName("error")[0].childNodes.length; i++)
4247
						sErrorMsg += oXMLDoc.getElementsByTagName("error")[0].childNodes[i].nodeValue;
4248
					document.getElementById("error_block").style.display = "";
4249
					setInnerHTML(document.getElementById("error_message"), sErrorMsg);
4250
					return false;
4251
				}
4252
4253
				// Get the progress bar right.
4254
				barTotal = debugItems * ', $upcontext['file_count'], ';
4255
				barDone = (debugItems * (curFile - 1)) + lastItem;
4256
4257
				updateStepProgress(barDone, barTotal, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4258
4259
				// Finally - update the time here as it shows the server is responding!
4260
				curTime = new Date();
4261
				iElapsed = (curTime.getTime() / 1000 - ', $upcontext['started'], ');
4262
				mins = parseInt(iElapsed / 60);
4263
				secs = parseInt(iElapsed - mins * 60);
4264
				setInnerHTML(document.getElementById("mins_elapsed"), mins);
4265
				setInnerHTML(document.getElementById("secs_elapsed"), secs);
4266
4267
				getNextItem();
4268
				return true;
4269
			}
4270
4271
			// What if we timeout?!
4272
			function retTimeout(attemptAgain)
4273
			{
4274
				// Oh noes...
4275
				if (!attemptAgain)
4276
				{
4277
					document.getElementById("error_block").style.display = "";
4278
					setInnerHTML(document.getElementById("error_message"), "Server has not responded for ', $timeLimitThreshold, ' seconds. It may be worth waiting a little longer or otherwise please click <a href=\"#\" onclick=\"retTimeout(true); return false;\">here<" + "/a> to try this step again");
4279
				}
4280
				else
4281
				{
4282
					document.getElementById("error_block").style.display = "none";
4283
					getNextItem();
4284
				}
4285
			}';
4286
4287
		// Start things off assuming we've not errored.
4288
		if (empty($upcontext['error_message']))
4289
			echo '
4290
			getNextItem();';
4291
4292
		echo '
4293
		//# sourceURL=dynamicScript-dbch.js 
4294
		</script>';
4295
	}
4296
	return;
4297
}
4298
4299
function template_database_xml()
4300
{
4301
	global $is_debug, $upcontext;
4302
4303
	echo '
4304
	<file num="', $upcontext['cur_file_num'], '" items="', $upcontext['total_items'], '" debug_items="', $upcontext['debug_items'], '">', $upcontext['cur_file_name'], '</file>
4305
	<item num="', $upcontext['current_item_num'], '">', $upcontext['current_item_name'], '</item>
4306
	<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>';
4307
4308
	if (!empty($upcontext['error_message']))
4309
		echo '
4310
	<error>', $upcontext['error_message'], '</error>';
4311
4312
	if (!empty($upcontext['error_string']))
4313
		echo '
4314
	<sql>', $upcontext['error_string'], '</sql>';
4315
4316
	if ($is_debug)
4317
		echo '
4318
	<curtime>', time(), '</curtime>';
4319
}
4320
4321
// Template for the UTF-8 conversion step. Basically a copy of the backup stuff with slight modifications....
4322 View Code Duplication
function template_convert_utf8()
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4323
{
4324
	global $upcontext, $support_js, $is_debug;
4325
4326
	echo '
4327
			<h3>Please wait while your database is converted to UTF-8. For large forums this may take some time!</h3>';
4328
4329
	echo '
4330
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4331
			<input type="hidden" name="utf8_done" id="utf8_done" value="0">
4332
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
4333
			<div id="debug_section" style="height: ', ($is_debug ? '195' : '12') , 'px; overflow: auto;">
4334
			<span id="debuginfo"></span>
4335
			</div>';
4336
4337
	// Done any tables so far?
4338
	if (!empty($upcontext['previous_tables']))
4339
		foreach ($upcontext['previous_tables'] as $table)
4340
			echo '
4341
			<br>Completed Table: &quot;', $table, '&quot;.';
4342
4343
	echo '
4344
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>';
4345
4346
	// If we dropped their index, let's let them know
4347
	if ($upcontext['cur_table_num'] == $upcontext['table_count'] && $upcontext['dropping_index'])
4348
		echo '
4349
			<br><span style="display:inline;">Please note that your fulltext index was dropped to facilitate the conversion and will need to be recreated.</span>';
4350
4351
	echo '
4352
			<br><span id="commess" style="font-weight: bold; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">Conversion Complete! Click Continue to Proceed.</span>';
4353
4354
	// Continue please!
4355
	$upcontext['continue'] = $support_js ? 2 : 1;
4356
4357
	// If javascript allows we want to do this using XML.
4358
	if ($support_js)
4359
	{
4360
		echo '
4361
		<script>
4362
			var lastTable = ', $upcontext['cur_table_num'], ';
4363
			function getNextTables()
4364
			{
4365
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onConversionUpdate);
4366
			}
4367
4368
			// Got an update!
4369
			function onConversionUpdate(oXMLDoc)
4370
			{
4371
				var sCurrentTableName = "";
4372
				var iTableNum = 0;
4373
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4374
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4375
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4376
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4377
4378
				// Update the page.
4379
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4380
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4381
				lastTable = iTableNum;
4382
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4383
4384
		// If debug flood the screen.
4385
		if ($is_debug)
4386
			echo '
4387
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4388
4389
				if (document.getElementById(\'debug_section\').scrollHeight)
4390
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4391
4392
		echo '
4393
				// Get the next update...
4394
				if (iTableNum == ', $upcontext['table_count'], ')
4395
				{
4396
					document.getElementById(\'commess\').style.display = "";
4397
					document.getElementById(\'current_tab_div\').style.display = "none";
4398
					document.getElementById(\'contbutt\').disabled = 0;
4399
					document.getElementById(\'utf8_done\').value = 1;
4400
				}
4401
				else
4402
					getNextTables();
4403
			}
4404
			getNextTables();
4405
		//# sourceURL=dynamicScript-conv.js 
4406
		</script>';
4407
	}
4408
}
4409
4410
function template_convert_xml()
4411
{
4412
	global $upcontext;
4413
4414
	echo '
4415
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4416
}
4417
4418
// Template for the database backup tool/
4419 View Code Duplication
function template_serialize_json()
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4420
{
4421
	global $upcontext, $support_js, $is_debug;
4422
4423
	echo '
4424
			<h3>Converting data from serialize to JSON...</h3>';
4425
4426
	echo '
4427
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4428
			<input type="hidden" name="json_done" id="json_done" value="0">
4429
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
4430
			<div id="debug_section" style="height: ', ($is_debug ? '195' : '12') , 'px; overflow: auto;">
4431
			<span id="debuginfo"></span>
4432
			</div>';
4433
4434
	// Dont any tables so far?
4435
	if (!empty($upcontext['previous_tables']))
4436
		foreach ($upcontext['previous_tables'] as $table)
4437
			echo '
4438
			<br>Completed Table: &quot;', $table, '&quot;.';
4439
4440
	echo '
4441
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
4442
			<br><span id="commess" style="font-weight: bold; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">Convert to JSON Complete! Click Continue to Proceed.</span>';
4443
4444
	// Try to make sure substep was reset.
4445
	if ($upcontext['cur_table_num'] == $upcontext['table_count'])
4446
		echo '
4447
			<input type="hidden" name="substep" id="substep" value="0">';
4448
4449
	// Continue please!
4450
	$upcontext['continue'] = $support_js ? 2 : 1;
4451
4452
	// If javascript allows we want to do this using XML.
4453
	if ($support_js)
4454
	{
4455
		echo '
4456
		<script>
4457
			var lastTable = ', $upcontext['cur_table_num'], ';
4458
			function getNextTables()
4459
			{
4460
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4461
			}
4462
4463
			// Got an update!
4464
			function onBackupUpdate(oXMLDoc)
4465
			{
4466
				var sCurrentTableName = "";
4467
				var iTableNum = 0;
4468
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4469
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4470
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4471
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4472
4473
				// Update the page.
4474
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4475
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4476
				lastTable = iTableNum;
4477
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4478
4479
		// If debug flood the screen.
4480
		if ($is_debug)
4481
			echo '
4482
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4483
4484
				if (document.getElementById(\'debug_section\').scrollHeight)
4485
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4486
4487
		echo '
4488
				// Get the next update...
4489
				if (iTableNum == ', $upcontext['table_count'], ')
4490
				{
4491
					document.getElementById(\'commess\').style.display = "";
4492
					document.getElementById(\'current_tab_div\').style.display = "none";
4493
					document.getElementById(\'contbutt\').disabled = 0;
4494
					document.getElementById(\'json_done\').value = 1;
4495
				}
4496
				else
4497
					getNextTables();
4498
			}
4499
			getNextTables();
4500
		//# sourceURL=dynamicScript-json.js 
4501
		</script>';
4502
	}
4503
}
4504
4505
function template_serialize_json_xml()
4506
{
4507
	global $upcontext;
4508
4509
	echo '
4510
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4511
}
4512
4513
function template_upgrade_complete()
4514
{
4515
	global $upcontext, $upgradeurl, $settings, $boardurl, $is_debug;
4516
4517
	echo '
4518
	<h3>That wasn\'t so hard, was it?  Now you are ready to use <a href="', $boardurl, '/index.php">your installation of SMF</a>.  Hope you like it!</h3>
4519
	<form action="', $boardurl, '/index.php">';
4520
4521
	if (!empty($upcontext['can_delete_script']))
4522
		echo '
4523
			<label for="delete_self"><input type="checkbox" id="delete_self" onclick="doTheDelete(this);" class="input_check"> Delete upgrade.php and its data files now</label> <em>(doesn\'t work on all servers).</em>
4524
			<script>
4525
				function doTheDelete(theCheck)
4526
				{
4527
					var theImage = document.getElementById ? document.getElementById("delete_upgrader") : document.all.delete_upgrader;
4528
4529
					theImage.src = "', $upgradeurl, '?delete=1&ts_" + (new Date().getTime());
4530
					theCheck.disabled = true;
4531
				}
4532
			</script>
4533
			<img src="', $settings['default_theme_url'], '/images/blank.png" alt="" id="delete_upgrader"><br>';
4534
4535
	$active = time() - $upcontext['started'];
4536
	$hours = floor($active / 3600);
4537
	$minutes = intval(($active / 60) % 60);
4538
	$seconds = intval($active % 60);
4539
4540
	if ($is_debug)
4541
	{
4542
		$totalTime = '';
4543
		if ($hours > 0)
4544
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
4545
		if ($minutes > 0)
4546
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
4547
		if ($seconds > 0)
4548
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
4549
	}
4550
4551
	if ($is_debug && !empty($totalTime))
4552
		echo '<br> Upgrade completed in ', $totalTime, '<br><br>';
4553
4554
	echo '<br>
4555
			If you had any problems with this upgrade, or have any problems using SMF, please don\'t hesitate to <a href="http://www.simplemachines.org/community/index.php">look to us for assistance</a>.<br>
4556
			<br>
4557
			Best of luck,<br>
4558
			Simple Machines';
4559
}
4560
4561
/**
4562
 * Convert MySQL (var)char ip col to binary
4563
 *
4564
 * @param string $targetTable The table to perform the operation on
4565
 * @param string $oldCol The old column to gather data from
4566
 * @param string $newCol The new column to put data in
4567
 * @param int $limit The amount of entries to handle at once.
4568
 * @param int $setSize The amount of entries after which to update the database.
4569
 *
4570
 * newCol needs to be a varbinary(16) null able field
4571
 * @return bool
4572
 */
4573
function MySQLConvertOldIp($targetTable, $oldCol, $newCol, $limit = 50000, $setSize = 100)
4574
{
4575
	global $smcFunc, $step_progress;
4576
4577
	$current_substep = $_GET['substep'];
4578
4579
	if (empty($_GET['a']))
4580
		$_GET['a'] = 0;
4581
	$step_progress['name'] = 'Converting ips';
4582
	$step_progress['current'] = $_GET['a'];
4583
4584
	// Skip this if we don't have the column
4585
	$request = $smcFunc['db_query']('', '
4586
		SHOW FIELDS
4587
		FROM {db_prefix}{raw:table}
4588
		WHERE Field = {string:name}',
4589
		array(
4590
			'table' => $targetTable,
4591
			'name' => $oldCol,
4592
	));
4593
	if ($smcFunc['db_num_rows']($request) !== 1)
4594
	{
4595
		$smcFunc['db_free_result']($request);
4596
		return;
4597
	}
4598
	$smcFunc['db_free_result']($request);
4599
4600
	//mysql default max length is 1mb http://dev.mysql.com/doc/refman/5.1/en/packet-too-large.html
4601
	$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...
4602
4603
	$is_done = false;
4604
	while (!$is_done)
4605
	{
4606
		// Keep looping at the current step.
4607
		nextSubStep($current_substep);
4608
4609
		$arIp = array();
4610
4611
		$request = $smcFunc['db_query']('', '
4612
			SELECT DISTINCT {raw:old_col}
4613
			FROM {db_prefix}{raw:table_name}
4614
			WHERE {raw:new_col} IS NULL
4615
			LIMIT {int:limit}',
4616
			array(
4617
				'old_col' => $oldCol,
4618
				'new_col' => $newCol,
4619
				'table_name' => $targetTable,
4620
				'empty' => '',
4621
				'limit' => $limit,
4622
		));
4623
		while ($row = $smcFunc['db_fetch_assoc']($request))
4624
			$arIp[] = $row[$oldCol];
4625
		$smcFunc['db_free_result']($request);
4626
4627
		// Special case, null ip could keep us in a loop.
4628
		if (is_null($arIp[0]))
4629
			unset($arIp[0]);
4630
4631
		if (empty($arIp))
4632
			$is_done = true;
4633
4634
		$updates = array();
4635
		$cases = array();
4636
		$count = count($arIp);
4637
		for ($i = 0; $i < $count; $i++)
4638
		{
4639
			$arIp[$i] = trim($arIp[$i]);
4640
4641
			if (empty($arIp[$i]))
4642
				continue;
4643
4644
			$updates['ip' . $i] = $arIp[$i];
4645
			$cases[$arIp[$i]] = 'WHEN ' . $oldCol . ' = {string:ip' . $i . '} THEN {inet:ip' . $i . '}';
4646
4647
			if ($setSize > 0 && $i % $setSize === 0)
4648
			{
4649
				if (count($updates) == 1)
4650
					continue;
4651
4652
				$updates['whereSet'] = array_values($updates);
4653
				$smcFunc['db_query']('', '
4654
					UPDATE {db_prefix}' . $targetTable . '
4655
					SET ' . $newCol . ' = CASE ' .
4656
					implode('
4657
						', $cases) . '
4658
						ELSE NULL
4659
					END
4660
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4661
					$updates
4662
				);
4663
4664
				$updates = array();
4665
				$cases = array();
4666
			}
4667
		}
4668
4669
		// Incase some extras made it through.
4670
		if (!empty($updates))
4671
		{
4672
			if (count($updates) == 1)
4673
			{
4674
				foreach ($updates as $key => $ip)
4675
				{
4676
					$smcFunc['db_query']('', '
4677
						UPDATE {db_prefix}' . $targetTable . '
4678
						SET ' . $newCol . ' = {inet:ip}
4679
						WHERE ' . $oldCol . ' = {string:ip}',
4680
						array(
4681
							'ip' => $ip
4682
					));
4683
				}
4684
			}
4685
			else
4686
			{
4687
				$updates['whereSet'] = array_values($updates);
4688
				$smcFunc['db_query']('', '
4689
					UPDATE {db_prefix}' . $targetTable . '
4690
					SET ' . $newCol . ' = CASE ' .
4691
					implode('
4692
						', $cases) . '
4693
						ELSE NULL
4694
					END
4695
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4696
					$updates
4697
				);
4698
			}
4699
		}
4700
		else
4701
			$is_done = true;
4702
4703
		$_GET['a'] += $limit;
4704
		$step_progress['current'] = $_GET['a'];
4705
	}
4706
4707
	unset($_GET['a']);
4708
}
4709
4710
/**
4711
 * Get the column info. This is basically the same as smf_db_list_columns but we get 1 column, force detail and other checks.
4712
 *
4713
 * @param string $targetTable The table to perform the operation on
4714
 * @param string $column The column we are looking for.
4715
 *
4716
 * @return array Info on the table.
4717
 */
4718
function upgradeGetColumnInfo($targetTable, $column)
4719
{
4720
 	global $smcFunc;
4721
4722
 	// This should already be here, but be safe.
4723
 	db_extend('packages');
4724
 
4725
 	$columns = $smcFunc['db_list_columns']($targetTable, true);
4726
4727
	if (isset($columns[$column]))
4728
		return $columns[$column];
4729
	else
4730
		return null;
4731
}
4732
4733
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...