Completed
Push — release-2.1 ( f30e04...3ac280 )
by Mert
07:11
created

upgrade.php ➔ template_upgrade_complete()   C

Complexity

Conditions 11
Paths 112

Size

Total Lines 47
Code Lines 24

Duplication

Lines 10
Ratio 21.28 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 11
eloc 24
c 1
b 0
f 1
nc 112
nop 0
dl 10
loc 47
rs 5.0769

How to fix   Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines http://www.simplemachines.org
8
 * @copyright 2016 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
$GLOBALS['required_php_version'] = '5.3.8';
19
$GLOBALS['required_mysql_version'] = '5.0.3';
20
21
$databases = array(
22
	'mysqli' => array(
23
		'name' => 'MySQLi',
24
		'version' => '5.0.3',
25
		'version_check' => 'global $db_connection; return min(mysqli_get_server_info($db_connection), mysqli_get_client_info());',
26
		'utf8_support' => true,
27
		'utf8_version' => '5.0.3',
28
		'utf8_version_check' => 'global $db_connection; return mysqli_get_server_info($db_connection);',
29
		'alter_support' => true,
30
	),
31
	'mysql' => array(
32
		'name' => 'MySQL',
33
		'version' => '5.0.3',
34
		'version_check' => 'return min(mysql_get_server_info(), mysql_get_client_info());',
35
		'utf8_support' => true,
36
		'utf8_version' => '5.0.3',
37
		'utf8_version_check' => 'return mysql_get_server_info();',
38
		'alter_support' => true,
39
	),
40
	'postgresql' => array(
41
		'name' => 'PostgreSQL',
42
		'version' => '8.0',
43
		'version_check' => '$version = pg_version(); return $version[\'client\'];',
44
		'always_has_db' => true,
45
	),
46
);
47
48
// General options for the script.
49
$timeLimitThreshold = 3;
50
$upgrade_path = dirname(__FILE__);
51
$upgradeurl = $_SERVER['PHP_SELF'];
52
// Where the SMF images etc are kept.
53
$smfsite = 'http://www.simplemachines.org/smf';
54
// Disable the need for admins to login?
55
$disable_security = false;
56
// How long, in seconds, must admin be inactive to allow someone else to run?
57
$upcontext['inactive_timeout'] = 10;
58
59
// All the steps in detail.
60
// Number,Name,Function,Progress Weight.
61
$upcontext['steps'] = array(
62
	0 => array(1, 'Login', 'WelcomeLogin', 2),
63
	1 => array(2, 'Upgrade Options', 'UpgradeOptions', 2),
64
	2 => array(3, 'Backup', 'BackupDatabase', 10),
65
	3 => array(4, 'Database Changes', 'DatabaseChanges', 50),
66
	4 => array(5, 'Convert to UTF-8', 'ConvertUtf8', 20),
67
	5 => array(6, 'Convert serialized strings to JSON', 'serialize_to_json', 10),
68
	// This is removed as it doesn't really work right at the moment.
69
	//4 => array(5, 'Cleanup Mods', 'CleanupMods', 10),
70
	6 => array(7, 'Delete Upgrade.php', 'DeleteUpgrade', 1),
71
);
72
// Just to remember which one has files in it.
73
$upcontext['database_step'] = 3;
74
@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...
75
if (!ini_get('safe_mode'))
76
{
77
	ini_set('mysql.connect_timeout', -1);
78
	ini_set('default_socket_timeout', 900);
79
}
80
// Clean the upgrade path if this is from the client.
81
if (!empty($_SERVER['argv']) && php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
82
	for ($i = 1; $i < $_SERVER['argc']; $i++)
83
	{
84
		if (preg_match('~^--path=(.+)$~', $_SERVER['argv'][$i], $match) != 0)
85
			$upgrade_path = substr($match[1], -1) == '/' ? substr($match[1], 0, -1) : $match[1];
86
	}
87
88
// Are we from the client?
89
if (php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
90
{
91
	$command_line = true;
92
	$disable_security = 1;
93
}
94
else
95
	$command_line = false;
96
97
// Load this now just because we can.
98
require_once($upgrade_path . '/Settings.php');
99
100
// Are we logged in?
101
if (isset($upgradeData))
102
{
103
	$upcontext['user'] = unserialize(base64_decode($upgradeData));
104
105
	// Check for sensible values.
106 View Code Duplication
	if (empty($upcontext['user']['started']) || $upcontext['user']['started'] < time() - 86400)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
107
		$upcontext['user']['started'] = time();
108 View Code Duplication
	if (empty($upcontext['user']['updated']) || $upcontext['user']['updated'] < time() - 86400)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
109
		$upcontext['user']['updated'] = 0;
110
111
	$upcontext['started'] = $upcontext['user']['started'];
112
	$upcontext['updated'] = $upcontext['user']['updated'];
113
114
	$is_debug = !empty($upcontext['user']['debug']) ? true : false;
115
}
116
117
// Nothing sensible?
118
if (empty($upcontext['updated']))
119
{
120
	$upcontext['started'] = time();
121
	$upcontext['updated'] = 0;
122
	$upcontext['user'] = array(
123
		'id' => 0,
124
		'name' => 'Guest',
125
		'pass' => 0,
126
		'started' => $upcontext['started'],
127
		'updated' => $upcontext['updated'],
128
	);
129
}
130
131
// Load up some essential data...
132
loadEssentialData();
133
134
// Are we going to be mimic'ing SSI at this point?
135
if (isset($_GET['ssi']))
136
{
137
	require_once($sourcedir . '/Errors.php');
138
	require_once($sourcedir . '/Logging.php');
139
	require_once($sourcedir . '/Load.php');
140
	require_once($sourcedir . '/Security.php');
141
	require_once($sourcedir . '/Subs-Package.php');
142
143
	loadUserSettings();
144
	loadPermissions();
145
}
146
147
if (!function_exists('un_htmlspecialchars'))
148
{
149
	function un_htmlspecialchars($string)
150
	{
151
		return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, ENT_QUOTES)) + array('&#039;' => '\'', '&nbsp;' => ' '));
152
	}
153
}
154
155
if (!function_exists('text2words'))
156
{
157
	function text2words($text)
158
	{
159
		// Step 1: Remove entities/things we don't consider words:
160
		$words = preg_replace('~(?:[\x0B\0\xA0\t\r\s\n(){}\\[\\]<>!@$%^*.,:+=`\~\?/\\\\]+|&(?:amp|lt|gt|quot);)+~', ' ', $text);
161
162
		// Step 2: Entities we left to letters, where applicable, lowercase.
163
		$words = preg_replace('~([^&\d]|^)[#;]~', '$1 ', un_htmlspecialchars(strtolower($words)));
164
165
		// Step 3: Ready to split apart and index!
166
		$words = explode(' ', $words);
167
		$returned_words = array();
168
		foreach ($words as $word)
169
		{
170
			$word = trim($word, '-_\'');
171
172
			if ($word != '')
173
				$returned_words[] = substr($word, 0, 20);
174
		}
175
176
		return array_unique($returned_words);
177
	}
178
}
179
180
if (!function_exists('clean_cache'))
181
{
182
	// Empty out the cache folder.
183
	function clean_cache($type = '')
184
	{
185
		global $cachedir, $sourcedir;
186
187
		// No directory = no game.
188
		if (!is_dir($cachedir))
189
			return;
190
191
		// Remove the files in SMF's own disk cache, if any
192
		$dh = opendir($cachedir);
193 View Code Duplication
		while ($file = readdir($dh))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
194
		{
195
			if ($file != '.' && $file != '..' && $file != 'index.php' && $file != '.htaccess' && (!$type || substr($file, 0, strlen($type)) == $type))
196
				@unlink($cachedir . '/' . $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...
197
		}
198
		closedir($dh);
199
200
		// Invalidate cache, to be sure!
201
		// ... as long as index.php can be modified, anyway.
202
		@touch($cachedir . '/' . 'index.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...
203
		clearstatcache();
204
	}
205
}
206
207
// MD5 Encryption.
208
if (!function_exists('md5_hmac'))
209
{
210
	function md5_hmac($data, $key)
211
	{
212
		if (strlen($key) > 64)
213
			$key = pack('H*', md5($key));
214
		$key = str_pad($key, 64, chr(0x00));
215
216
		$k_ipad = $key ^ str_repeat(chr(0x36), 64);
217
		$k_opad = $key ^ str_repeat(chr(0x5c), 64);
218
219
		return md5($k_opad . pack('H*', md5($k_ipad . $data)));
220
	}
221
}
222
223
// http://www.faqs.org/rfcs/rfc959.html
224
if (!class_exists('ftp_connection'))
225
{
226
	class ftp_connection
0 ignored issues
show
Comprehensibility Best Practice introduced by
The type ftp_connection has been defined more than once; this definition is ignored, only the first definition in other/install.php (L1663-2000) is considered.

This check looks for classes that have been defined more than once.

If you can, we would recommend to use standard object-oriented programming techniques. For example, to avoid multiple types, it might make sense to create a common interface, and then multiple, different implementations for that interface.

This also has the side-effect of providing you with better IDE auto-completion, static analysis and also better OPCode caching from PHP.

Loading history...
227
	{
228
		var $connection = 'no_connection', $error = false, $last_message, $pasv = array();
229
230
		// Create a new FTP connection...
231
		function ftp_connection($ftp_server, $ftp_port = 21, $ftp_user = 'anonymous', $ftp_pass = '[email protected]')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
232
		{
233
			if ($ftp_server !== null)
234
				$this->connect($ftp_server, $ftp_port, $ftp_user, $ftp_pass);
235
		}
236
237 View Code Duplication
		function connect($ftp_server, $ftp_port = 21, $ftp_user = 'anonymous', $ftp_pass = '[email protected]')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method 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...
238
		{
239
			if (substr($ftp_server, 0, 6) == 'ftp://')
240
				$ftp_server = substr($ftp_server, 6);
241
			elseif (substr($ftp_server, 0, 7) == 'ftps://')
242
				$ftp_server = 'ssl://' . substr($ftp_server, 7);
243
			if (substr($ftp_server, 0, 7) == 'http://')
244
				$ftp_server = substr($ftp_server, 7);
245
			$ftp_server = strtr($ftp_server, array('/' => '', ':' => '', '@' => ''));
246
247
			// Connect to the FTP server.
248
			$this->connection = @fsockopen($ftp_server, $ftp_port, $err, $err, 5);
249
			if (!$this->connection)
250
			{
251
				$this->error = 'bad_server';
0 ignored issues
show
Documentation Bug introduced by
The property $error was declared of type boolean, but 'bad_server' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
252
				return;
253
			}
254
255
			// Get the welcome message...
256
			if (!$this->check_response(220))
257
			{
258
				$this->error = 'bad_response';
0 ignored issues
show
Documentation Bug introduced by
The property $error was declared of type boolean, but 'bad_response' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
259
				return;
260
			}
261
262
			// Send the username, it should ask for a password.
263
			fwrite($this->connection, 'USER ' . $ftp_user . "\r\n");
264
			if (!$this->check_response(331))
265
			{
266
				$this->error = 'bad_username';
0 ignored issues
show
Documentation Bug introduced by
The property $error was declared of type boolean, but 'bad_username' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
267
				return;
268
			}
269
270
			// Now send the password... and hope it goes okay.
271
			fwrite($this->connection, 'PASS ' . $ftp_pass . "\r\n");
272
			if (!$this->check_response(230))
273
			{
274
				$this->error = 'bad_password';
0 ignored issues
show
Documentation Bug introduced by
The property $error was declared of type boolean, but 'bad_password' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
275
				return;
276
			}
277
		}
278
279 View Code Duplication
		function chdir($ftp_path)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method 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...
280
		{
281
			if (!is_resource($this->connection))
282
				return false;
283
284
			// No slash on the end, please...
285
			if (substr($ftp_path, -1) == '/' && $ftp_path !== '/')
286
				$ftp_path = substr($ftp_path, 0, -1);
287
288
			fwrite($this->connection, 'CWD ' . $ftp_path . "\r\n");
289
			if (!$this->check_response(250))
290
			{
291
				$this->error = 'bad_path';
0 ignored issues
show
Documentation Bug introduced by
The property $error was declared of type boolean, but 'bad_path' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
292
				return false;
293
			}
294
295
			return true;
296
		}
297
298 View Code Duplication
		function chmod($ftp_file, $chmod)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method 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...
299
		{
300
			if (!is_resource($this->connection))
301
				return false;
302
303
			// Convert the chmod value from octal (0777) to text ("777").
304
			fwrite($this->connection, 'SITE CHMOD ' . decoct($chmod) . ' ' . $ftp_file . "\r\n");
305
			if (!$this->check_response(200))
306
			{
307
				$this->error = 'bad_file';
0 ignored issues
show
Documentation Bug introduced by
The property $error was declared of type boolean, but 'bad_file' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
308
				return false;
309
			}
310
311
			return true;
312
		}
313
314 View Code Duplication
		function unlink($ftp_file)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method 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...
315
		{
316
			// We are actually connected, right?
317
			if (!is_resource($this->connection))
318
				return false;
319
320
			// Delete file X.
321
			fwrite($this->connection, 'DELE ' . $ftp_file . "\r\n");
322
			if (!$this->check_response(250))
323
			{
324
				fwrite($this->connection, 'RMD ' . $ftp_file . "\r\n");
325
326
				// Still no love?
327
				if (!$this->check_response(250))
328
				{
329
					$this->error = 'bad_file';
0 ignored issues
show
Documentation Bug introduced by
The property $error was declared of type boolean, but 'bad_file' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
330
					return false;
331
				}
332
			}
333
334
			return true;
335
		}
336
337 View Code Duplication
		function check_response($desired)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method 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...
338
		{
339
			// Wait for a response that isn't continued with -, but don't wait too long.
340
			$time = time();
341
			do
342
				$this->last_message = fgets($this->connection, 1024);
343
			while (substr($this->last_message, 3, 1) != ' ' && time() - $time < 5);
344
345
			// Was the desired response returned?
346
			return is_array($desired) ? in_array(substr($this->last_message, 0, 3), $desired) : substr($this->last_message, 0, 3) == $desired;
347
		}
348
349 View Code Duplication
		function passive()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method 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...
350
		{
351
			// We can't create a passive data connection without a primary one first being there.
352
			if (!is_resource($this->connection))
353
				return false;
354
355
			// Request a passive connection - this means, we'll talk to you, you don't talk to us.
356
			@fwrite($this->connection, 'PASV' . "\r\n");
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...
357
			$time = time();
358
			do
359
				$response = fgets($this->connection, 1024);
360
			while (substr($response, 3, 1) != ' ' && time() - $time < 5);
361
362
			// If it's not 227, we weren't given an IP and port, which means it failed.
363
			if (substr($response, 0, 4) != '227 ')
364
			{
365
				$this->error = 'bad_response';
0 ignored issues
show
Documentation Bug introduced by
The property $error was declared of type boolean, but 'bad_response' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
366
				return false;
367
			}
368
369
			// Snatch the IP and port information, or die horribly trying...
370
			if (preg_match('~\((\d+),\s*(\d+),\s*(\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+))\)~', $response, $match) == 0)
371
			{
372
				$this->error = 'bad_response';
373
				return false;
374
			}
375
376
			// This is pretty simple - store it for later use ;).
377
			$this->pasv = array('ip' => $match[1] . '.' . $match[2] . '.' . $match[3] . '.' . $match[4], 'port' => $match[5] * 256 + $match[6]);
378
379
			return true;
380
		}
381
382
		function create_file($ftp_file)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
383
		{
384
			// First, we have to be connected... very important.
385
			if (!is_resource($this->connection))
386
				return false;
387
388
			// I'd like one passive mode, please!
389
			if (!$this->passive())
390
				return false;
391
392
			// Seems logical enough, so far...
393
			fwrite($this->connection, 'STOR ' . $ftp_file . "\r\n");
394
395
			// Okay, now we connect to the data port.  If it doesn't work out, it's probably "file already exists", etc.
396
			$fp = @fsockopen($this->pasv['ip'], $this->pasv['port'], $err, $err, 5);
397
			if (!$fp || !$this->check_response(150))
398
			{
399
				$this->error = 'bad_file';
0 ignored issues
show
Documentation Bug introduced by
The property $error was declared of type boolean, but 'bad_file' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
400
				@fclose($fp);
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...
401
				return false;
402
			}
403
404
			// This may look strange, but we're just closing it to indicate a zero-byte upload.
405
			fclose($fp);
406
			if (!$this->check_response(226))
407
			{
408
				$this->error = 'bad_response';
0 ignored issues
show
Documentation Bug introduced by
The property $error was declared of type boolean, but 'bad_response' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
409
				return false;
410
			}
411
412
			return true;
413
		}
414
415 View Code Duplication
		function list_dir($ftp_path = '', $search = false)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method 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...
416
		{
417
			// Are we even connected...?
418
			if (!is_resource($this->connection))
419
				return false;
420
421
			// Passive... non-agressive...
422
			if (!$this->passive())
423
				return false;
424
425
			// Get the listing!
426
			fwrite($this->connection, 'LIST -1' . ($search ? 'R' : '') . ($ftp_path == '' ? '' : ' ' . $ftp_path) . "\r\n");
427
428
			// Connect, assuming we've got a connection.
429
			$fp = @fsockopen($this->pasv['ip'], $this->pasv['port'], $err, $err, 5);
430
			if (!$fp || !$this->check_response(array(150, 125)))
431
			{
432
				$this->error = 'bad_response';
0 ignored issues
show
Documentation Bug introduced by
The property $error was declared of type boolean, but 'bad_response' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
433
				@fclose($fp);
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...
434
				return false;
435
			}
436
437
			// Read in the file listing.
438
			$data = '';
439
			while (!feof($fp))
440
				$data .= fread($fp, 4096);
441
			fclose($fp);
442
443
			// Everything go okay?
444
			if (!$this->check_response(226))
445
			{
446
				$this->error = 'bad_response';
447
				return false;
448
			}
449
450
			return $data;
451
		}
452
453 View Code Duplication
		function locate($file, $listing = null)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method 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...
454
		{
455
			if ($listing === null)
456
				$listing = $this->list_dir('', true);
457
			$listing = explode("\n", $listing);
458
459
			@fwrite($this->connection, 'PWD' . "\r\n");
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...
460
			$time = time();
461
			do
462
				$response = fgets($this->connection, 1024);
463
			while (substr($response, 3, 1) != ' ' && time() - $time < 5);
464
465
			// Check for 257!
466
			if (preg_match('~^257 "(.+?)" ~', $response, $match) != 0)
467
				$current_dir = strtr($match[1], array('""' => '"'));
468
			else
469
				$current_dir = '';
470
471
			for ($i = 0, $n = count($listing); $i < $n; $i++)
472
			{
473
				if (trim($listing[$i]) == '' && isset($listing[$i + 1]))
474
				{
475
					$current_dir = substr(trim($listing[++$i]), 0, -1);
476
					$i++;
477
				}
478
479
				// Okay, this file's name is:
480
				$listing[$i] = $current_dir . '/' . trim(strlen($listing[$i]) > 30 ? strrchr($listing[$i], ' ') : $listing[$i]);
481
482
				if (substr($file, 0, 1) == '*' && substr($listing[$i], -(strlen($file) - 1)) == substr($file, 1))
483
					return $listing[$i];
484
				if (substr($file, -1) == '*' && substr($listing[$i], 0, strlen($file) - 1) == substr($file, 0, -1))
485
					return $listing[$i];
486
				if (basename($listing[$i]) == $file || $listing[$i] == $file)
487
					return $listing[$i];
488
			}
489
490
			return false;
491
		}
492
493
		function create_dir($ftp_dir)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
494
		{
495
			// We must be connected to the server to do something.
496
			if (!is_resource($this->connection))
497
				return false;
498
499
			// Make this new beautiful directory!
500
			fwrite($this->connection, 'MKD ' . $ftp_dir . "\r\n");
501
			if (!$this->check_response(257))
502
			{
503
				$this->error = 'bad_file';
0 ignored issues
show
Documentation Bug introduced by
The property $error was declared of type boolean, but 'bad_file' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
504
				return false;
505
			}
506
507
			return true;
508
		}
509
510 View Code Duplication
		function detect_path($filesystem_path, $lookup_file = null)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method 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...
511
		{
512
			$username = '';
513
514
			if (isset($_SERVER['DOCUMENT_ROOT']))
515
			{
516
				if (preg_match('~^/home[2]?/([^/]+?)/public_html~', $_SERVER['DOCUMENT_ROOT'], $match))
517
				{
518
					$username = $match[1];
519
520
					$path = strtr($_SERVER['DOCUMENT_ROOT'], array('/home/' . $match[1] . '/' => '', '/home2/' . $match[1] . '/' => ''));
521
522
					if (substr($path, -1) == '/')
523
						$path = substr($path, 0, -1);
524
525
					if (strlen(dirname($_SERVER['PHP_SELF'])) > 1)
526
						$path .= dirname($_SERVER['PHP_SELF']);
527
				}
528
				elseif (substr($filesystem_path, 0, 9) == '/var/www/')
529
					$path = substr($filesystem_path, 8);
530
				else
531
					$path = strtr(strtr($filesystem_path, array('\\' => '/')), array($_SERVER['DOCUMENT_ROOT'] => ''));
532
			}
533
			else
534
				$path = '';
535
536
			if (is_resource($this->connection) && $this->list_dir($path) == '')
537
			{
538
				$data = $this->list_dir('', true);
539
540
				if ($lookup_file === null)
541
					$lookup_file = $_SERVER['PHP_SELF'];
542
543
				$found_path = dirname($this->locate('*' . basename(dirname($lookup_file)) . '/' . basename($lookup_file), $data));
544
				if ($found_path == false)
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $found_path of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
545
					$found_path = dirname($this->locate(basename($lookup_file)));
546
				if ($found_path != false)
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $found_path of type string to the boolean false. If you are specifically checking for a non-empty string, consider using the more explicit !== '' instead.
Loading history...
547
					$path = $found_path;
548
			}
549
			elseif (is_resource($this->connection))
550
				$found_path = true;
551
552
			return array($username, $path, isset($found_path));
553
		}
554
555
		function close()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
556
		{
557
			// Goodbye!
558
			fwrite($this->connection, 'QUIT' . "\r\n");
559
			fclose($this->connection);
560
561
			return true;
562
		}
563
	}
564
}
565
566
// Don't do security check if on Yabbse
567
if (!isset($modSettings['smfVersion']))
568
	$disable_security = true;
569
570
// This only exists if we're on SMF ;)
571
if (isset($modSettings['smfVersion']))
572
{
573
	$request = $smcFunc['db_query']('', '
574
		SELECT variable, value
575
		FROM {db_prefix}themes
576
		WHERE id_theme = {int:id_theme}
577
			AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
578
		array(
579
			'id_theme' => 1,
580
			'theme_url' => 'theme_url',
581
			'theme_dir' => 'theme_dir',
582
			'images_url' => 'images_url',
583
			'db_error_skip' => true,
584
		)
585
	);
586 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
587
		$modSettings[$row['variable']] = $row['value'];
588
	$smcFunc['db_free_result']($request);
589
}
590
591
if (!isset($modSettings['theme_url']))
592
{
593
	$modSettings['theme_dir'] = $boarddir . '/Themes/default';
594
	$modSettings['theme_url'] = 'Themes/default';
595
	$modSettings['images_url'] = 'Themes/default/images';
596
}
597
if (!isset($settings['default_theme_url']))
598
	$settings['default_theme_url'] = $modSettings['theme_url'];
599
if (!isset($settings['default_theme_dir']))
600
	$settings['default_theme_dir'] = $modSettings['theme_dir'];
601
602
$upcontext['is_large_forum'] = (empty($modSettings['smfVersion']) || $modSettings['smfVersion'] <= '1.1 RC1') && !empty($modSettings['totalMessages']) && $modSettings['totalMessages'] > 75000;
603
// Default title...
604
$upcontext['page_title'] = isset($modSettings['smfVersion']) ? 'Updating Your SMF Install!' : 'Upgrading from YaBB SE!';
605
606
// Have we got tracking data - if so use it (It will be clean!)
607
if (isset($_GET['data']))
608
{
609
	global $is_debug;
610
611
	$upcontext['upgrade_status'] = safe_unserialize(base64_decode($_GET['data']));
612
	$upcontext['current_step'] = $upcontext['upgrade_status']['curstep'];
613
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
614
	$upcontext['rid'] = $upcontext['upgrade_status']['rid'];
615
	$support_js = $upcontext['upgrade_status']['js'];
616
617
	// Only set this if the upgrader status says so.
618
	if (empty($is_debug))
619
		$is_debug = $upcontext['upgrade_status']['debug'];
620
621
	// Load the language.
622 View Code Duplication
	if (file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
623
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
624
}
625
// Set the defaults.
626
else
627
{
628
	$upcontext['current_step'] = 0;
629
	$upcontext['rid'] = mt_rand(0, 5000);
630
	$upcontext['upgrade_status'] = array(
631
		'curstep' => 0,
632
		'lang' => isset($_GET['lang']) ? $_GET['lang'] : basename($language, '.lng'),
633
		'rid' => $upcontext['rid'],
634
		'pass' => 0,
635
		'debug' => 0,
636
		'js' => 0,
637
	);
638
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
639
}
640
641
// If this isn't the first stage see whether they are logging in and resuming.
642
if ($upcontext['current_step'] != 0 || !empty($upcontext['user']['step']))
643
	checkLogin();
644
645
if ($command_line)
646
	cmdStep0();
647
648
// Don't error if we're using xml.
649
if (isset($_GET['xml']))
650
	$upcontext['return_error'] = true;
651
652
// Loop through all the steps doing each one as required.
653
$upcontext['overall_percent'] = 0;
654
foreach ($upcontext['steps'] as $num => $step)
655
{
656
	if ($num >= $upcontext['current_step'])
657
	{
658
		// The current weight of this step in terms of overall progress.
659
		$upcontext['step_weight'] = $step[3];
660
		// Make sure we reset the skip button.
661
		$upcontext['skip'] = false;
662
663
		// We cannot proceed if we're not logged in.
664
		if ($num != 0 && !$disable_security && $upcontext['user']['pass'] != $upcontext['upgrade_status']['pass'])
665
		{
666
			$upcontext['steps'][0][2]();
667
			break;
668
		}
669
670
		// Call the step and if it returns false that means pause!
671 View Code Duplication
		if (function_exists($step[2]) && $step[2]() === false)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
672
			break;
673
		elseif (function_exists($step[2]))
674
			$upcontext['current_step']++;
675
	}
676
	$upcontext['overall_percent'] += $step[3];
677
}
678
679
upgradeExit();
680
681
// Exit the upgrade script.
682
function upgradeExit($fallThrough = false)
683
{
684
	global $upcontext, $upgradeurl, $boarddir, $command_line, $is_debug;
685
686
	// Save where we are...
687
	if (!empty($upcontext['current_step']) && !empty($upcontext['user']['id']))
688
	{
689
		$upcontext['user']['step'] = $upcontext['current_step'];
690
		$upcontext['user']['substep'] = $_GET['substep'];
691
		$upcontext['user']['updated'] = time();
692
		$upcontext['debug'] = $is_debug;
693
		$upgradeData = base64_encode(safe_serialize($upcontext['user']));
694
		copy($boarddir . '/Settings.php', $boarddir . '/Settings_bak.php');
695
		changeSettings(array('upgradeData' => '"' . $upgradeData . '"'));
696
		updateLastError();
697
	}
698
699
	// Handle the progress of the step, if any.
700
	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
701
	{
702
		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
703
		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
704
	}
705
	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
706
707
	// We usually dump our templates out.
708
	if (!$fallThrough)
709
	{
710
		// This should not happen my dear... HELP ME DEVELOPERS!!
711
		if (!empty($command_line))
712
		{
713
			if (function_exists('debug_print_backtrace'))
714
				debug_print_backtrace();
715
716
			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.';
717
			flush();
718
			die();
719
		}
720
721
		if (!isset($_GET['xml']))
722
			template_upgrade_above();
723
		else
724
		{
725
			header('Content-Type: text/xml; charset=UTF-8');
726
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
727
			$upcontext['get_data'] = array();
728
			foreach ($_GET as $k => $v)
729
			{
730
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
731
				{
732
					$upcontext['get_data'][$k] = $v;
733
				}
734
			}
735
			template_xml_above();
736
		}
737
738
		// Call the template.
739
		if (isset($upcontext['sub_template']))
740
		{
741
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
742
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(safe_serialize($upcontext['upgrade_status']));
743
744
			// Custom stuff to pass back?
745
			if (!empty($upcontext['query_string']))
746
				$upcontext['form_url'] .= $upcontext['query_string'];
747
748
			call_user_func('template_' . $upcontext['sub_template']);
749
		}
750
751
		// Was there an error?
752
		if (!empty($upcontext['forced_error_message']))
753
			echo $upcontext['forced_error_message'];
754
755
		// Show the footer.
756
		if (!isset($_GET['xml']))
757
			template_upgrade_below();
758
		else
759
			template_xml_below();
760
	}
761
762
763
	if (!empty($command_line) && $is_debug)
764
	{
765
		$active = time() - $upcontext['started'];
766
		$hours = floor($active / 3600);     
767
		$minutes = intval(($active / 60) % 60);        
768
		$seconds = intval($active % 60);     
769
770
		$totalTime = '';
771
		if ($hours > 0)
772
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's':'') . ' ';        
773
		if ($minutes > 0)
774
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's':'') . ' ';        
775
		if ($seconds > 0)
776
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's':'') . ' ';        
777
778
		if (!empty($totalTime))
779
			echo "\n" . 'Upgrade completed in ' . $totalTime . "\n";
780
	}
781
782
	// Bang - gone!
783
	die();
784
}
785
786
// Used to direct the user to another location.
787
function redirectLocation($location, $addForm = true)
788
{
789
	global $upgradeurl, $upcontext, $command_line;
790
791
	// Command line users can't be redirected.
792
	if ($command_line)
793
		upgradeExit(true);
794
795
	// Are we providing the core info?
796
	if ($addForm)
797
	{
798
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
799
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(safe_serialize($upcontext['upgrade_status'])) . $location;
800
	}
801
802
	while (@ob_end_clean());
803
	header('Location: ' . strtr($location, array('&amp;' => '&')));
804
805
	// Exit - saving status as we go.
806
	upgradeExit(true);
807
}
808
809
// Load all essential data and connect to the DB as this is pre SSI.php
810
function loadEssentialData()
811
{
812
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection, $db_prefix, $db_character_set, $db_type;
813
	global $modSettings, $sourcedir, $smcFunc;
814
815
	// Do the non-SSI stuff...
816
	if (function_exists('set_magic_quotes_runtime'))
817
		@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...
818
819
	error_reporting(E_ALL);
820
	define('SMF', 1);
821
822
	// Start the session.
823
	if (@ini_get('session.save_handler') == 'user')
824
		@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...
825
	@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...
826
827
	if (empty($smcFunc))
828
		$smcFunc = array();
829
830
	// We need this for authentication and some upgrade code
831
	require_once($sourcedir . '/Subs-Auth.php');
832
833
	$smcFunc['strtolower'] = 'smf_strtolower';
834
835
836
	// Initialize everything...
837
	initialize_inputs();
838
839
	// Get the database going!
840
	if (empty($db_type))
841
		$db_type = 'mysql';
842
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
843
	{
844
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
845
846
		// Make the connection...
847
		$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, array('non_fatal' => true));
848
849
		// Oh dear god!!
850
		if ($db_connection === null)
851
			die('Unable to connect to database - please check username and password are correct in Settings.php');
852
853
		if (($db_type == 'mysql' || $db_type == 'mysqli') && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
854
			$smcFunc['db_query']('', '
855
			SET NAMES ' . $db_character_set,
856
			array(
857
				'db_error_skip' => true,
858
			)
859
		);
860
861
		// Load the modSettings data...
862
		$request = $smcFunc['db_query']('', '
863
			SELECT variable, value
864
			FROM {db_prefix}settings',
865
			array(
866
				'db_error_skip' => true,
867
			)
868
		);
869
		$modSettings = array();
870 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
871
			$modSettings[$row['variable']] = $row['value'];
872
		$smcFunc['db_free_result']($request);
873
	}
874
	else
875
	{
876
		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.');
877
	}
878
879
	require_once($sourcedir . '/Subs.php');
880
881
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
882
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
883
	{
884
		require_once($sourcedir . '/QueryString.php');
885
		cleanRequest();
886
	}
887
888
	if (!isset($_GET['substep']))
889
		$_GET['substep'] = 0;
890
}
891
892
function initialize_inputs()
0 ignored issues
show
Best Practice introduced by
The function initialize_inputs() has been defined more than once; this definition is ignored, only the first definition in other/install.php (L145-256) 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...
893
{
894
	global $start_time, $upcontext, $db_type;
895
896
	$start_time = time();
897
898
	umask(0);
899
900
	// Fun.  Low PHP version...
901
	if (!isset($_GET))
902
	{
903
		$GLOBALS['_GET']['step'] = 0;
904
		return;
905
	}
906
907
	ob_start();
908
909
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
910
	ignore_user_abort(true);
911
912
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
913
	if (isset($_GET['delete']))
914
	{
915
		@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...
916
917
		$type = ($db_type == 'mysqli' ? 'mysql' : $db_type);
918
919
		// And the extra little files ;).
920
		@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...
921
		@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...
922
		@unlink(dirname(__FILE__) . '/upgrade_2-0_' . $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...
923
		@unlink(dirname(__FILE__) . '/upgrade_2-1_' . $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...
924
925
		$dh = opendir(dirname(__FILE__));
926
		while ($file = readdir($dh))
927
		{
928
			if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
929
				@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...
930
		}
931
		closedir($dh);
932
933
		// Legacy files while we're at it. NOTE: We only touch files we KNOW shouldn't be there.
934
		// 1.1 Sources files not in 2.0+
935
		@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...
936
		// 1.1 Templates that don't exist any more (e.g. renamed)
937
		@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...
938
		@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...
939
		// 1.1 JS files were stored in the main theme folder, but in 2.0+ are in the scripts/ folder
940
		@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...
941
		@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...
942
		@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...
943
		@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...
944
		@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...
945
946
		// 2.0 Sources files not in 2.1+
947
		@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...
948
		@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...
949
950
		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');
951
		exit;
952
	}
953
954
	// Anybody home?
955 View Code Duplication
	if (!isset($_GET['xml']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
956
	{
957
		$upcontext['remote_files_available'] = false;
958
		$test = @fsockopen('www.simplemachines.org', 80, $errno, $errstr, 1);
959
		if ($test)
960
			$upcontext['remote_files_available'] = true;
961
		@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...
962
	}
963
964
	// Something is causing this to happen, and it's annoying.  Stop it.
965
	$temp = 'upgrade_php?step';
966
	while (strlen($temp) > 4)
967
	{
968
		if (isset($_GET[$temp]))
969
			unset($_GET[$temp]);
970
		$temp = substr($temp, 1);
971
	}
972
973
	// Force a step, defaulting to 0.
974
	$_GET['step'] = (int) @$_GET['step'];
975
	$_GET['substep'] = (int) @$_GET['substep'];
976
}
977
978
// Step 0 - Let's welcome them in and ask them to login!
979
function WelcomeLogin()
980
{
981
	global $boarddir, $sourcedir, $modSettings, $cachedir, $upgradeurl, $upcontext;
982
	global $smcFunc, $db_type, $databases, $txt, $boardurl;
983
984
	$upcontext['sub_template'] = 'welcome_message';
985
986
	$type = ($db_type == 'mysqli' ? 'mysql' : $db_type);
987
988
	// Check for some key files - one template, one language, and a new and an old source file.
989
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
990
		&& @file_exists($sourcedir . '/QueryString.php')
991
		&& @file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')
992
		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $type . '.sql');
993
994
	// Need legacy scripts?
995 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.1)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
996
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_2-0_' . $type . '.sql');
997 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.0)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
998
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-1.sql');
999 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 1.1)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1000
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
1001
1002
	// This needs to exist!
1003
	if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
1004
		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>]');
1005
	else
1006
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
1007
1008
	if (!$check)
1009
		// 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.
1010
		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.');
1011
1012
	// Do they meet the install requirements?
1013
	if (!php_version_check())
1014
		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.');
1015
1016
	if (!db_version_check())
1017
		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.');
1018
1019
	// Do some checks to make sure they have proper privileges
1020
	db_extend('packages');
1021
1022
	// CREATE
1023
	$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');
1024
1025
	// ALTER
1026
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
1027
1028
	// DROP
1029
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
1030
1031
	// Sorry... we need CREATE, ALTER and DROP
1032 View Code Duplication
	if (!$create || !$alter || !$drop)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1033
		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.');
1034
1035
	// Do a quick version spot check.
1036
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
1037
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
1038
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
1039
		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.');
1040
1041
	// What absolutely needs to be writable?
1042
	$writable_files = array(
1043
		$boarddir . '/Settings.php',
1044
		$boarddir . '/Settings_bak.php',
1045
	);
1046
1047
	// Do we need to add this setting?
1048
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
1049
1050
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] .'/custom_avatar';
1051
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl .'/custom_avatar';
1052
1053
	// This little fellow has to cooperate...
1054
	quickFileWritable($custom_av_dir);
1055
1056
	// Are we good now?
1057
	if(!is_writable($custom_av_dir))
1058
		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));
1059
	elseif ($need_settings_update)
1060
	{
1061
		if (!function_exists('cache_put_data'))
1062
			require_once($sourcedir . '/Load.php');
1063
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
1064
		updateSettings(array('custom_avatar_url' => $custom_av_url));
1065
	}
1066
1067
	require_once($sourcedir . '/Security.php');
1068
1069
	// Check the cache directory.
1070
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
1071
	if (!file_exists($cachedir_temp))
1072
		@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...
1073
	if (!file_exists($cachedir_temp))
1074
		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.');
1075
1076
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
1077
		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>.');
1078
	elseif (!isset($_GET['skiplang']))
1079
	{
1080
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
1081
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
1082
1083
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
1084
			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>]');
1085
	}
1086
1087
	if (!makeFilesWritable($writable_files))
1088
		return false;
1089
1090
	// Check agreement.txt. (it may not exist, in which case $boarddir must be writable.)
1091 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1092
		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.');
1093
1094
	// Upgrade the agreement.
1095
	elseif (isset($modSettings['agreement']))
1096
	{
1097
		$fp = fopen($boarddir . '/agreement.txt', 'w');
1098
		fwrite($fp, $modSettings['agreement']);
1099
		fclose($fp);
1100
	}
1101
1102
	// We're going to check that their board dir setting is right in case they've been moving stuff around.
1103
	if (strtr($boarddir, array('/' => '', '\\' => '')) != strtr(dirname(__FILE__), array('/' => '', '\\' => '')))
1104
		$upcontext['warning'] = '
1105
			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>
1106
			<ul>
1107
				<li>Board Directory: ' . $boarddir . '</li>
1108
				<li>Source Directory: ' . $boarddir . '</li>
1109
				<li>Cache Directory: ' . $cachedir_temp . '</li>
1110
			</ul>
1111
			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.';
1112
1113
	// Either we're logged in or we're going to present the login.
1114
	if (checkLogin())
1115
		return true;
1116
1117
	$upcontext += createToken('login');
1118
1119
	return false;
1120
}
1121
1122
// Step 0.5: Does the login work?
1123
function checkLogin()
1124
{
1125
	global $modSettings, $upcontext, $disable_security;
1126
	global $smcFunc, $db_type, $support_js;
1127
1128
	// Are we trying to login?
1129
	if (isset($_POST['contbutt']) && (!empty($_POST['user']) || $disable_security))
1130
	{
1131
		// If we've disabled security pick a suitable name!
1132
		if (empty($_POST['user']))
1133
			$_POST['user'] = 'Administrator';
1134
1135
		// Before 2.0 these column names were different!
1136
		$oldDB = false;
1137
		if (empty($db_type) || $db_type == 'mysql' || $db_type == 'mysqli')
1138
		{
1139
			$request = $smcFunc['db_query']('', '
1140
				SHOW COLUMNS
1141
				FROM {db_prefix}members
1142
				LIKE {string:member_name}',
1143
				array(
1144
					'member_name' => 'memberName',
1145
					'db_error_skip' => true,
1146
				)
1147
			);
1148
			if ($smcFunc['db_num_rows']($request) != 0)
1149
				$oldDB = true;
1150
			$smcFunc['db_free_result']($request);
1151
		}
1152
1153
		// Get what we believe to be their details.
1154
		if (!$disable_security)
1155
		{
1156
			if ($oldDB)
1157
				$request = $smcFunc['db_query']('', '
1158
					SELECT id_member, memberName AS member_name, passwd, id_group,
1159
					additionalGroups AS additional_groups, lngfile
1160
					FROM {db_prefix}members
1161
					WHERE memberName = {string:member_name}',
1162
					array(
1163
						'member_name' => $_POST['user'],
1164
						'db_error_skip' => true,
1165
					)
1166
				);
1167
			else
1168
				$request = $smcFunc['db_query']('', '
1169
					SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile
1170
					FROM {db_prefix}members
1171
					WHERE member_name = {string:member_name}',
1172
					array(
1173
						'member_name' => $_POST['user'],
1174
						'db_error_skip' => true,
1175
					)
1176
				);
1177
			if ($smcFunc['db_num_rows']($request) != 0)
1178
			{
1179
				list ($id_member, $name, $password, $id_group, $addGroups, $user_language) = $smcFunc['db_fetch_row']($request);
1180
1181
				$groups = explode(',', $addGroups);
1182
				$groups[] = $id_group;
1183
1184
				foreach ($groups as $k => $v)
1185
					$groups[$k] = (int) $v;
1186
1187
				$sha_passwd = sha1(strtolower($name) . un_htmlspecialchars($_REQUEST['passwrd']));
1188
			}
1189
			else
1190
				$upcontext['username_incorrect'] = true;
1191
			$smcFunc['db_free_result']($request);
1192
		}
1193
		$upcontext['username'] = $_POST['user'];
1194
1195
		// Track whether javascript works!
1196
		if (!empty($_POST['js_works']))
1197
		{
1198
			$upcontext['upgrade_status']['js'] = 1;
1199
			$support_js = 1;
1200
		}
1201
		else
1202
			$support_js = 0;
1203
1204
		// Note down the version we are coming from.
1205
		if (!empty($modSettings['smfVersion']) && empty($upcontext['user']['version']))
1206
			$upcontext['user']['version'] = $modSettings['smfVersion'];
1207
1208
		// Didn't get anywhere?
1209
		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']))
0 ignored issues
show
Bug introduced by
The variable $sha_passwd 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...
1210
		{
1211
			// MD5?
1212
			$md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user']));
1213
			if ($md5pass != $password)
0 ignored issues
show
Bug introduced by
The variable $password 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...
1214
			{
1215
				$upcontext['password_failed'] = true;
1216
				// Disable the hashing this time.
1217
				$upcontext['disable_login_hashing'] = true;
1218
			}
1219
		}
1220
1221
		if ((empty($upcontext['password_failed']) && !empty($name)) || $disable_security)
1222
		{
1223
			// Set the password.
1224
			if (!$disable_security)
1225
			{
1226
				// Do we actually have permission?
1227
				if (!in_array(1, $groups))
1228
				{
1229
					$request = $smcFunc['db_query']('', '
1230
						SELECT permission
1231
						FROM {db_prefix}permissions
1232
						WHERE id_group IN ({array_int:groups})
1233
							AND permission = {string:admin_forum}',
1234
						array(
1235
							'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...
1236
							'admin_forum' => 'admin_forum',
1237
							'db_error_skip' => true,
1238
						)
1239
					);
1240
					if ($smcFunc['db_num_rows']($request) == 0)
1241
						return throw_error('You need to be an admin to perform an upgrade!');
1242
					$smcFunc['db_free_result']($request);
1243
				}
1244
1245
				$upcontext['user']['id'] = $id_member;
0 ignored issues
show
Bug introduced by
The variable $id_member 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...
1246
				$upcontext['user']['name'] = $name;
1247
			}
1248
			else
1249
			{
1250
				$upcontext['user']['id'] = 1;
1251
				$upcontext['user']['name'] = 'Administrator';
1252
			}
1253
			$upcontext['user']['pass'] = mt_rand(0,60000);
1254
			// This basically is used to match the GET variables to Settings.php.
1255
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
1256
1257
			// Set the language to that of the user?
1258
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
1259
			{
1260
				$user_language = basename($user_language, '.lng');
1261
				$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
1262
				preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
1263
1264
				if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
1265
					$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'] . '.';
1266
				elseif (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . basename($user_language, '.lng') . '.php'))
1267
					$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'] . '.';
1268
				else
1269
				{
1270
					// Set this as the new language.
1271
					$upcontext['language'] = $user_language;
1272
					$upcontext['upgrade_status']['lang'] = $upcontext['language'];
1273
1274
					// Include the file.
1275
					require_once($modSettings['theme_dir'] . '/languages/Install.' . $user_language . '.php');
1276
				}
1277
			}
1278
1279
			// If we're resuming set the step and substep to be correct.
1280
			if (isset($_POST['cont']))
1281
			{
1282
				$upcontext['current_step'] = $upcontext['user']['step'];
1283
				$_GET['substep'] = $upcontext['user']['substep'];
1284
			}
1285
1286
			return true;
1287
		}
1288
	}
1289
1290
	return false;
1291
}
1292
1293
// Step 1: Do the maintenance and backup.
1294
function UpgradeOptions()
1295
{
1296
	global $db_prefix, $command_line, $modSettings, $is_debug, $smcFunc, $packagesdir, $tasksdir;
1297
	global $boarddir, $boardurl, $sourcedir, $maintenance, $cachedir, $upcontext, $db_type, $db_server, $db_last_error;
1298
1299
	$upcontext['sub_template'] = 'upgrade_options';
1300
	$upcontext['page_title'] = 'Upgrade Options';
1301
1302
	db_extend('packages');
1303
	$upcontext['karma_installed'] = array('good' => false, 'bad' => false);
1304
	$member_columns = $smcFunc['db_list_columns']('{db_prefix}members');
1305
1306
	$upcontext['karma_installed']['good'] = in_array('karma_good', $member_columns);
1307
	$upcontext['karma_installed']['bad'] = in_array('karma_bad', $member_columns);
1308
1309
	unset($member_columns);
1310
1311
	// If we've not submitted then we're done.
1312
	if (empty($_POST['upcont']))
1313
		return false;
1314
1315
	require_once($sourcedir . '/Subs-Admin.php');
1316
	updateSettingsFile(array('image_proxy_secret' => '\'' . substr(sha1(mt_rand()), 0, 20) . '\''));
1317
1318
	// Firstly, if they're enabling SM stat collection just do it.
1319
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']))
1320
	{
1321
		// Attempt to register the site etc.
1322
		$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
1323
		if ($fp)
1324
		{
1325
			$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
1326
			$out .= 'Host: www.simplemachines.org' . "\r\n";
1327
			$out .= 'Connection: Close' . "\r\n\r\n";
1328
			fwrite($fp, $out);
1329
1330
			$return_data = '';
1331
			while (!feof($fp))
1332
				$return_data .= fgets($fp, 128);
1333
1334
			fclose($fp);
1335
1336
			// Get the unique site ID.
1337
			preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);
1338
1339 View Code Duplication
			if (!empty($ID[1]))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1340
				$smcFunc['db_insert']('replace',
1341
					$db_prefix . 'settings',
1342
					array('variable' => 'string', 'value' => 'string'),
1343
					array('allow_sm_stats', $ID[1]),
1344
					array('variable')
1345
				);
1346
		}
1347
	}
1348
	else
1349
		$smcFunc['db_query']('', '
1350
			DELETE FROM {db_prefix}settings
1351
			WHERE variable = {string:allow_sm_stats}',
1352
			array(
1353
				'allow_sm_stats' => 'allow_sm_stats',
1354
				'db_error_skip' => true,
1355
			)
1356
		);
1357
1358
	// Deleting old karma stuff?
1359
	if (!empty($_POST['delete_karma']))
1360
	{
1361
		// Delete old settings vars.
1362
		$smcFunc['db_query']('', '
1363
			DELETE FROM {db_prefix}settings
1364
			WHERE variable IN ({array_string:karma_vars})',
1365
			array(
1366
				'karma_vars' => array('karmaMode', 'karmaTimeRestrictAdmins', 'karmaWaitTime', 'karmaMinPosts', 'karmaLabel', 'karmaSmiteLabel', 'karmaApplaudLabel'),
1367
			)
1368
		);
1369
1370
		// Cleaning up old karma member settings.
1371
		if ($upcontext['karma_installed']['good'])
1372
			$smcFunc['db_query']('', '
1373
				ALTER TABLE {db_prefix}members
1374
				DROP karma_good',
1375
				array()
1376
			);
1377
1378
		// Does karma bad was enable?
1379
		if ($upcontext['karma_installed']['bad'])
1380
			$smcFunc['db_query']('', '
1381
				ALTER TABLE {db_prefix}members
1382
				DROP karma_bad',
1383
				array()
1384
			);
1385
1386
		// Cleaning up old karma permissions.
1387
		$smcFunc['db_query']('', '
1388
			DELETE FROM {db_prefix}permissions
1389
			WHERE permission = {string:karma_vars}',
1390
			array(
1391
				'karma_vars' => 'karma_edit',
1392
			)
1393
		);
1394
	}
1395
1396
	// Emptying the error log?
1397
	if (!empty($_POST['empty_error']))
1398
		$smcFunc['db_query']('truncate_table', '
1399
			TRUNCATE {db_prefix}log_errors',
1400
			array(
1401
			)
1402
		);
1403
1404
	$changes = array();
1405
1406
	// If we're overriding the language follow it through.
1407 View Code Duplication
	if (isset($_GET['lang']) && file_exists($modSettings['theme_dir'] . '/languages/index.' . $_GET['lang'] . '.php'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1408
		$changes['language'] = '\'' . $_GET['lang'] . '\'';
1409
1410
	if (!empty($_POST['maint']))
1411
	{
1412
		$changes['maintenance'] = '2';
1413
		// Remember what it was...
1414
		$upcontext['user']['main'] = $maintenance;
1415
1416
		if (!empty($_POST['maintitle']))
1417
		{
1418
			$changes['mtitle'] = '\'' . addslashes($_POST['maintitle']) . '\'';
1419
			$changes['mmessage'] = '\'' . addslashes($_POST['mainmessage']) . '\'';
1420
		}
1421
		else
1422
		{
1423
			$changes['mtitle'] = '\'Upgrading the forum...\'';
1424
			$changes['mmessage'] = '\'Don\\\'t worry, we will be back shortly with an updated forum.  It will only be a minute ;).\'';
1425
		}
1426
	}
1427
1428
	if ($command_line)
1429
		echo ' * Updating Settings.php...';
1430
1431
	// Backup the current one first.
1432
	copy($boarddir . '/Settings.php', $boarddir . '/Settings_bak.php');
1433
1434
	// Fix some old paths.
1435 View Code Duplication
	if (substr($boarddir, 0, 1) == '.')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1436
		$changes['boarddir'] = '\'' . fixRelativePath($boarddir) . '\'';
1437
1438 View Code Duplication
	if (substr($sourcedir, 0, 1) == '.')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1439
		$changes['sourcedir'] = '\'' . fixRelativePath($sourcedir) . '\'';
1440
1441
	if (empty($cachedir) || substr($cachedir, 0, 1) == '.')
1442
		$changes['cachedir'] = '\'' . fixRelativePath($boarddir) . '/cache\'';
1443
1444
	// Not had the database type added before?
1445
	if (empty($db_type))
1446
		$changes['db_type'] = 'mysql';
1447
1448
	// For now we offer a option, this may change in future versions when mysql is completely removed.
1449
	if (!empty($_POST['convertMysql']) && $db_type == 'mysql')
1450
		$changes['db_type'] = '\'mysqli\'';
1451
1452
	// If they have a "host:port" setup for the host, split that into separate values
1453
	// You should never have a : in the hostname if you're not on MySQL, but better safe than sorry
1454
	if (strpos($db_server, ':') !== false && ($db_type == 'mysql' || $db_type == 'mysqli'))
1455
	{
1456
		list($db_server, $db_port) = explode(':', $db_server);
1457
1458
		$changes['db_server'] = '\'' . $db_server . '\'';
1459
1460
		// Only set this if we're not using the default port
1461
		if ($db_port != ini_get('mysql' . ($db_type == 'mysqli' || !empty($_POST['convertMysql']) ? 'i' : '') . '.default_port'))
1462
			$changes['db_port'] = (int) $db_port;
1463
	}
1464
	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...
1465
	{
1466
		// If db_port is set and is the same as the default, set it to ''
1467
		if ($db_type == 'mysql' || $db_type == 'mysqli')
1468
		{
1469
			if ($db_port == ini_get('mysql' . ($db_type == 'mysqli' || !empty($_POST['convertMysql']) ? 'i' : '') . '.default_port'))
1470
				$changes['db_port'] = '\'\'';
1471
			elseif ($db_type == 'postgresql' && $db_port == 5432)
1472
				$changes['db_port'] = '\'\'';
1473
		}
1474
	}
1475
1476
	// Maybe we haven't had this option yet?
1477
	if (empty($packagesdir))
1478
		$changes['packagesdir'] = '\'' . fixRelativePath($boarddir) . '/Packages\'';
1479
1480
	// Add support for $tasksdir var.
1481
	if (empty($tasksdir))
1482
		$changes['tasksdir'] = '\'' . fixRelativePath($sourcedir) . '/tasks\'';
1483
1484
	// @todo Maybe change the cookie name if going to 1.1, too?
1485
1486
	// Update Settings.php with the new settings.
1487
	changeSettings($changes);
1488
1489
	// Back up again before we do anything else, just in case...
1490
	copy($boarddir . '/Settings.php', $boarddir . '/Settings_bak.php');
1491
1492
	// Read the contents of the file in as a string
1493
	$settings_file = file_get_contents($boarddir . '/Settings.php');
1494
1495
	// Look to see if the new error-catching section is there...
1496
	if (stripos($settings_file, 'if (file_exists(dirname(__FILE__) . \'/db_last_error.php\'))') === false)
1497
	{
1498
		// This is what we want to add...
1499
		$error_catching_header = '
1500
########## Error-Catching ##########
1501
# Note: You shouldn\'t touch these settings.';
1502
1503
		$error_catching = '
1504
if (file_exists(dirname(__FILE__) . \'/db_last_error.php\'))
1505
	include(dirname(__FILE__) . \'/db_last_error.php\');
1506
1507
if (!isset($db_last_error))
1508
{
1509
	// File does not exist so lets try to create it
1510
	file_put_contents(dirname(__FILE__) . \'/db_last_error.php\', \'<\' . \'?\' . "php\n" . \'$db_last_error = 0;\' . "\n" . \'?\' . \'>\');
1511
	$db_last_error = 0;
1512
}
1513
';
1514
		// Before we go any further, check to see if the original code is there first...
1515
		if (stripos('########## Error-Catching ##########', $settings_file !== false))
1516
		{
1517
			$found_old = true;
1518
			// Replace the old line with the new code - assuming the header is already there
1519
			$settings_file = str_replace('$db_last_error = ' . $db_last_error . ';', $error_catching, $settings_file);
1520
		}
1521
		// What about just the db_last_error line?
1522
		elseif (stripos('$db_last_error =', $settings_file !== false))
1523
		{
1524
			$found_old = true;
1525
			// Replace the old line with the new code
1526
			$settings_file = str_replace('$db_last_error = ' . $db_last_error . ';', $error_catching_header . $error_catching, $settings_file);
1527
		}
1528
		else
1529
		{
1530
			$found_old = false;
1531
			// We want the comments as well as the code...
1532
			$error_catching = $error_catching_header . $error_catching;
1533
		}
1534
1535
		// Blank out the file - done to fix a oddity with some servers.
1536
		$fp = fopen($boarddir . '/Settings.php', 'w');
1537
		fclose($fp);
1538
1539
		// Open the file for writing
1540
		$file = fopen($boarddir . '/Settings.php', 'r+');
1541
1542
		// Write the original contents...
1543
		fwrite($file, $settings_file);
1544
1545
		// If we didn't find the old code, add the new code at the end instead...
1546
		if (!$found_old)
1547
		{
1548
			// Go to the position two bytes before the end - in front of the closing PHP tag
1549
			fseek($file, -2, SEEK_END);
1550
1551
			// Append our new data - the extra line break above will prevent us from breaking things...
1552
			fwrite($file, $error_catching);
1553
		}
1554
1555
		// Close the file
1556
		fclose($file);
1557
	}
1558
1559
	if ($command_line)
1560
		echo ' Successful.' . "\n";
1561
1562
	// Are we doing debug?
1563
	if (isset($_POST['debug']))
1564
	{
1565
		$upcontext['upgrade_status']['debug'] = true;
1566
		$is_debug = true;
1567
	}
1568
1569
	// If we're not backing up then jump one.
1570
	if (empty($_POST['backup']))
1571
		$upcontext['current_step']++;
1572
1573
	// If we've got here then let's proceed to the next step!
1574
	return true;
1575
}
1576
1577
// Backup the database - why not...
1578
function BackupDatabase()
1579
{
1580
	global $upcontext, $db_prefix, $command_line, $is_debug, $support_js, $file_steps, $smcFunc;
1581
1582
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1583
	$upcontext['page_title'] = 'Backup Database';
1584
1585
	// Done it already - js wise?
1586
	if (!empty($_POST['backup_done']))
1587
		return true;
1588
1589
	// Some useful stuff here.
1590
	db_extend();
1591
1592
	// Might need this as well
1593
	db_extend('packages');
1594
1595
	// Get all the table names.
1596
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1597
	$db = preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? strtr($match[1], array('`' => '')) : false;
1598
	$tables = $smcFunc['db_list_tables']($db, $filter);
1599
1600
	$table_names = array();
1601
	foreach ($tables as $table)
1602
		if (substr($table, 0, 7) !== 'backup_')
1603
			$table_names[] = $table;
1604
1605
	$upcontext['table_count'] = count($table_names);
1606
	$upcontext['cur_table_num'] = $_GET['substep'];
1607
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1608
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1609
	// For non-java auto submit...
1610
	$file_steps = $upcontext['table_count'];
1611
1612
	// What ones have we already done?
1613 View Code Duplication
	foreach ($table_names as $id => $table)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1614
		if ($id < $_GET['substep'])
1615
			$upcontext['previous_tables'][] = $table;
1616
1617
	if ($command_line)
1618
		echo 'Backing Up Tables.';
1619
1620
	// If we don't support javascript we backup here.
1621
	if (!$support_js || isset($_GET['xml']))
1622
	{
1623
		// Backup each table!
1624
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1625
		{
1626
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1627
			$upcontext['cur_table_num'] = $substep + 1;
1628
1629
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1630
1631
			// Do we need to pause?
1632
			nextSubstep($substep);
1633
1634
			backupTable($table_names[$substep]);
1635
1636
			// If this is XML to keep it nice for the user do one table at a time anyway!
1637
			if (isset($_GET['xml']))
1638
				return upgradeExit();
1639
		}
1640
1641
		if ($is_debug && $command_line)
1642
		{
1643
			echo "\n" . ' Successful.\'' . "\n";
1644
			flush();
1645
		}
1646
		$upcontext['step_progress'] = 100;
1647
1648
		$_GET['substep'] = 0;
1649
		// Make sure we move on!
1650
		return true;
1651
	}
1652
1653
	// Either way next place to post will be database changes!
1654
	$_GET['substep'] = 0;
1655
	return false;
1656
}
1657
1658
// Backup one table...
1659
function backupTable($table)
1660
{
1661
	global $is_debug, $command_line, $db_prefix, $smcFunc;
1662
1663
	if ($command_line)
1664
	{
1665
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1666
		flush();
1667
	}
1668
1669
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1670
1671
	if ($command_line)
1672
		echo ' done.';
1673
}
1674
1675
// Step 2: Everything.
1676
function DatabaseChanges()
1677
{
1678
	global $db_prefix, $modSettings, $command_line, $smcFunc;
1679
	global $upcontext, $support_js, $db_type;
1680
1681
	// Have we just completed this?
1682
	if (!empty($_POST['database_done']))
1683
		return true;
1684
1685
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1686
	$upcontext['page_title'] = 'Database Changes';
1687
1688
	$type = ($db_type == 'mysqli' ? 'mysql' : $db_type);
1689
1690
	// All possible files.
1691
	// Name, <version, insert_on_complete
1692
	$files = array(
1693
		array('upgrade_1-0.sql', '1.1', '1.1 RC0'),
1694
		array('upgrade_1-1.sql', '2.0', '2.0 a'),
1695
		array('upgrade_2-0_' . $type . '.sql', '2.1', '2.1 dev0'),
1696
		array('upgrade_2-1_' . $type . '.sql', '3.0', SMF_VERSION),
1697
	);
1698
1699
	// How many files are there in total?
1700
	if (isset($_GET['filecount']))
1701
		$upcontext['file_count'] = (int) $_GET['filecount'];
1702
	else
1703
	{
1704
		$upcontext['file_count'] = 0;
1705
		foreach ($files as $file)
1706
		{
1707
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1708
				$upcontext['file_count']++;
1709
		}
1710
	}
1711
1712
	// Do each file!
1713
	$did_not_do = count($files) - $upcontext['file_count'];
1714
	$upcontext['step_progress'] = 0;
1715
	$upcontext['cur_file_num'] = 0;
1716
	foreach ($files as $file)
1717
	{
1718
		if ($did_not_do)
1719
			$did_not_do--;
1720
		else
1721
		{
1722
			$upcontext['cur_file_num']++;
1723
			$upcontext['cur_file_name'] = $file[0];
1724
			// Do we actually need to do this still?
1725
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1726
			{
1727
				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1728 View Code Duplication
				if ($nextFile)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1729
				{
1730
					// Only update the version of this if complete.
1731
					$smcFunc['db_insert']('replace',
1732
						$db_prefix . 'settings',
1733
						array('variable' => 'string', 'value' => 'string'),
1734
						array('smfVersion', $file[2]),
1735
						array('variable')
1736
					);
1737
1738
					$modSettings['smfVersion'] = $file[2];
1739
				}
1740
1741
				// If this is XML we only do this stuff once.
1742
				if (isset($_GET['xml']))
1743
				{
1744
					// Flag to move on to the next.
1745
					$upcontext['completed_step'] = true;
1746
					// Did we complete the whole file?
1747
					if ($nextFile)
1748
						$upcontext['current_debug_item_num'] = -1;
1749
					return upgradeExit();
1750
				}
1751
				elseif ($support_js)
1752
					break;
1753
			}
1754
			// Set the progress bar to be right as if we had - even if we hadn't...
1755
			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1756
		}
1757
	}
1758
1759
	$_GET['substep'] = 0;
1760
	// So the template knows we're done.
1761
	if (!$support_js)
1762
	{
1763
		$upcontext['changes_complete'] = true;
1764
1765
		return true;
1766
	}
1767
	return false;
1768
}
1769
1770
// Clean up any mods installed...
1771
function CleanupMods()
1772
{
1773
	global $db_prefix, $upcontext, $boarddir, $packagesdir, $settings, $smcFunc, $command_line;
1774
1775
	// Sorry. Not supported for command line users.
1776
	if ($command_line)
1777
		return true;
1778
1779
	// Skipping first?
1780
	if (!empty($_POST['skip']))
1781
	{
1782
		unset($_POST['skip']);
1783
		return true;
1784
	}
1785
1786
	// If we get here withOUT SSI we need to redirect to ensure we get it!
1787
	if (!isset($_GET['ssi']) || !function_exists('mktree'))
1788
		redirectLocation('&ssi=1');
1789
1790
	$upcontext['sub_template'] = 'clean_mods';
1791
	$upcontext['page_title'] = 'Cleanup Modifications';
1792
1793
	// This can be skipped.
1794
	$upcontext['skip'] = true;
1795
1796
	// If we're on the second redirect continue...
1797
	if (isset($_POST['cleandone2']))
1798
		return true;
1799
1800
	// Do we already know about some writable files?
1801
	if (isset($_POST['writable_files']))
1802
	{
1803
		$writable_files = safe_unserialize(base64_decode($_POST['writable_files']));
1804
		if (!makeFilesWritable($writable_files))
1805
		{
1806
			// What have we left?
1807
			$upcontext['writable_files'] = $writable_files;
1808
			return false;
1809
		}
1810
	}
1811
1812
	// Make sure we have some sort of packages directory.
1813
	if (!isset($packagesdir))
1814
		$packagesdir = $boarddir . '/Packages';
1815
1816
	// Load all theme paths....
1817
	$request = $smcFunc['db_query']('', '
1818
		SELECT id_theme, variable, value
1819
		FROM {db_prefix}themes
1820
		WHERE id_member = {int:id_member}
1821
			AND variable IN ({string:theme_dir}, {string:images_url})',
1822
		array(
1823
			'id_member' => 0,
1824
			'theme_dir' => 'theme_dir',
1825
			'images_url' => 'images_url',
1826
			'db_error_skip' => true,
1827
		)
1828
	);
1829
	$theme_paths = array();
1830
	while ($row = $smcFunc['db_fetch_assoc']($request))
1831
	{
1832
		if ($row['id_theme'] == 1)
1833
			$settings['default_' . $row['variable']] = $row['value'];
1834
		elseif ($row['variable'] == 'theme_dir')
1835
			$theme_paths[$row['id_theme']][$row['variable']] = $row['value'];
1836
	}
1837
	$smcFunc['db_free_result']($request);
1838
1839
	// Are there are mods installed that may need uninstalling?
1840
	$request = $smcFunc['db_query']('', '
1841
		SELECT id_install, filename, name, themes_installed, version
1842
		FROM {db_prefix}log_packages
1843
		WHERE install_state = {int:installed}
1844
		ORDER BY time_installed DESC',
1845
		array(
1846
			'installed' => 1,
1847
			'db_error_skip' => true,
1848
		)
1849
	);
1850
	$upcontext['packages'] = array();
1851
	while ($row = $smcFunc['db_fetch_assoc']($request))
1852
	{
1853
		// Work out the status.
1854
		if (!file_exists($packagesdir . '/' . $row['filename']))
1855
		{
1856
			$status = 'Missing';
1857
			$status_color = 'red';
1858
			$result = 'Removed';
1859
		}
1860
		else
1861
		{
1862
			$status = 'Installed';
1863
			$status_color = 'green';
1864
			$result = 'No Action Needed';
1865
		}
1866
1867
		$upcontext['packages'][$row['id_install']] = array(
1868
			'id' => $row['id_install'],
1869
			'themes' => explode(',', $row['themes_installed']),
1870
			'name' => $row['name'],
1871
			'filename' => $row['filename'],
1872
			'missing_file' => file_exists($packagesdir . '/' . $row['filename']) ? 0 : 1,
1873
			'files' => array(),
1874
			'file_count' => 0,
1875
			'status' => $status,
1876
			'result' => $result,
1877
			'color' => $status_color,
1878
			'version' => $row['version'],
1879
			'needs_removing' => false,
1880
		);
1881
	}
1882
	$smcFunc['db_free_result']($request);
1883
1884
	// Don't carry on if there are none.
1885
	if (empty($upcontext['packages']))
1886
		return true;
1887
1888
	// Setup some basics.
1889
	if (!empty($upcontext['user']['version']))
1890
		$_SESSION['version_emulate'] = $upcontext['user']['version'];
1891
1892
	// Before we get started, don't report notice errors.
1893
	$oldErrorReporting = error_reporting(E_ALL ^ E_NOTICE);
1894
1895
	if (!mktree($packagesdir . '/temp', 0755))
1896
	{
1897
		deltree($packagesdir . '/temp', false);
1898
		if (!mktree($packagesdir . '/temp', 0777))
1899
		{
1900
			deltree($packagesdir . '/temp', false);
1901
			// @todo Error here - plus chmod!
1902
		}
1903
	}
1904
1905
	// Anything which reinstalled should not have its entry removed.
1906
	$reinstall_worked = array();
1907
1908
	// We're gonna be doing some removin'
1909
	$test = isset($_POST['cleandone']) ? false : true;
1910
	foreach ($upcontext['packages'] as $id => $package)
1911
	{
1912
		// Can't do anything about this....
1913
		if ($package['missing_file'])
1914
			continue;
1915
1916
		// Not testing *and* this wasn't checked?
1917
		if (!$test && (!isset($_POST['remove']) || !isset($_POST['remove'][$id])))
1918
			continue;
1919
1920
		// What are the themes this was installed into?
1921
		$cur_theme_paths = array();
1922
		foreach ($theme_paths as $tid => $data)
1923
			if ($tid != 1 && in_array($tid, $package['themes']))
1924
				$cur_theme_paths[$tid] = $data;
1925
1926
		// Get the modifications data if applicable.
1927
		$filename = $package['filename'];
1928
		$packageInfo = getPackageInfo($filename);
1929
		if (!is_array($packageInfo))
1930
			continue;
1931
1932
		$info = parsePackageInfo($packageInfo['xml'], $test, 'uninstall');
1933
		// Also get the reinstall details...
1934
		if (isset($_POST['remove']))
1935
			$infoInstall = parsePackageInfo($packageInfo['xml'], true);
1936
1937 View Code Duplication
		if (is_file($packagesdir . '/' . $filename))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1938
			read_tgz_file($packagesdir . '/' . $filename, $packagesdir . '/temp');
1939
		else
1940
			copytree($packagesdir . '/' . $filename, $packagesdir . '/temp');
1941
1942
		// Work out how we uninstall...
1943
		$files = array();
1944
		foreach ($info as $change)
1945
		{
1946
			// Work out two things:
1947
			// 1) Whether it's installed at the moment - and if so whether its fully installed, and:
1948
			// 2) Whether it could be installed on the new version.
1949
			if ($change['type'] == 'modification')
1950
			{
1951
				$contents = @file_get_contents($packagesdir . '/temp/' . $upcontext['base_path'] . $change['filename']);
1952
				if ($change['boardmod'])
1953
					$results = parseBoardMod($contents, $test, $change['reverse'], $cur_theme_paths);
1954
				else
1955
					$results = parseModification($contents, $test, $change['reverse'], $cur_theme_paths);
1956
1957
				foreach ($results as $action)
1958
				{
1959
					// Something we can remove? Probably means it existed!
1960
					if (($action['type'] == 'replace' || $action['type'] == 'append' || (!empty($action['filename']) && $action['type'] == 'failure')) && !in_array($action['filename'], $files))
1961
						$files[] = $action['filename'];
1962
					if ($action['type'] == 'failure')
1963
					{
1964
						$upcontext['packages'][$id]['needs_removing'] = true;
1965
						$upcontext['packages'][$id]['status'] = 'Reinstall Required';
1966
						$upcontext['packages'][$id]['color'] = '#FD6435';
1967
					}
1968
				}
1969
			}
1970
		}
1971
1972
		// Store this info for the template as appropriate.
1973
		$upcontext['packages'][$id]['files'] = $files;
1974
		$upcontext['packages'][$id]['file_count'] = count($files);
1975
1976
		// If we've done something save the changes!
1977
		if (!$test)
1978
			package_flush_cache();
1979
1980
		// Are we attempting to reinstall this thing?
1981
		if (isset($_POST['remove']) && !$test && isset($infoInstall))
1982
		{
1983
			// Need to extract again I'm afraid.
1984 View Code Duplication
			if (is_file($packagesdir . '/' . $filename))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1985
				read_tgz_file($packagesdir . '/' . $filename, $packagesdir . '/temp');
1986
			else
1987
				copytree($packagesdir . '/' . $filename, $packagesdir . '/temp');
1988
1989
			$errors = false;
1990
			$upcontext['packages'][$id]['result'] = 'Removed';
1991
			foreach ($infoInstall as $change)
1992
			{
1993
				if ($change['type'] == 'modification')
1994
				{
1995
					$contents = @file_get_contents($packagesdir . '/temp/' . $upcontext['base_path'] . $change['filename']);
1996 View Code Duplication
					if ($change['boardmod'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1997
						$results = parseBoardMod($contents, true, $change['reverse'], $cur_theme_paths);
1998
					else
1999
						$results = parseModification($contents, true, $change['reverse'], $cur_theme_paths);
2000
2001
					// Are there any errors?
2002
					foreach ($results as $action)
2003
						if ($action['type'] == 'failure')
2004
							$errors = true;
2005
				}
2006
			}
2007
			if (!$errors)
2008
			{
2009
				$reinstall_worked[] = $id;
2010
				$upcontext['packages'][$id]['result'] = 'Reinstalled';
2011
				$upcontext['packages'][$id]['color'] = 'green';
2012
				foreach ($infoInstall as $change)
2013
				{
2014
					if ($change['type'] == 'modification')
2015
					{
2016
						$contents = @file_get_contents($packagesdir . '/temp/' . $upcontext['base_path'] . $change['filename']);
2017 View Code Duplication
						if ($change['boardmod'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2018
							$results = parseBoardMod($contents, false, $change['reverse'], $cur_theme_paths);
0 ignored issues
show
Unused Code introduced by
$results 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...
2019
						else
2020
							$results = parseModification($contents, false, $change['reverse'], $cur_theme_paths);
0 ignored issues
show
Unused Code introduced by
$results 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...
2021
					}
2022
				}
2023
2024
				// Save the changes.
2025
				package_flush_cache();
2026
			}
2027
		}
2028
	}
2029
2030
	// Put errors back on a sec.
2031
	error_reporting($oldErrorReporting);
2032
2033
	// Check everything is writable.
2034
	if ($test && !empty($upcontext['packages']))
2035
	{
2036
		$writable_files = array();
2037
		foreach ($upcontext['packages'] as $package)
2038
		{
2039
			if (!empty($package['files']))
2040
				foreach ($package['files'] as $file)
2041
					$writable_files[] = $file;
2042
		}
2043
2044
		if (!empty($writable_files))
2045
		{
2046
			$writable_files = array_unique($writable_files);
2047
			$upcontext['writable_files'] = $writable_files;
2048
2049
			if (!makeFilesWritable($writable_files))
2050
				return false;
2051
		}
2052
	}
2053
2054
	if (file_exists($packagesdir . '/temp'))
2055
		deltree($packagesdir . '/temp');
2056
2057
	// Removing/Reinstalling any packages?
2058
	if (isset($_POST['remove']))
2059
	{
2060
		$deletes = array();
2061
		foreach ($_POST['remove'] as $id => $dummy)
2062
		{
2063
			if (!in_array((int) $id, $reinstall_worked))
2064
				$deletes[] = (int) $id;
2065
		}
2066
2067
		if (!empty($deletes))
2068
			upgrade_query( '
2069
				UPDATE ' . $db_prefix . 'log_packages
2070
				SET install_state = 0
2071
				WHERE id_install IN (' . implode(',', $deletes) . ')');
2072
2073
		// Ensure we don't lose our changes!
2074
		package_put_contents($packagesdir . '/installed.list', time());
2075
2076
		$upcontext['sub_template'] = 'cleanup_done';
2077
		return false;
2078
	}
2079
	else
2080
	{
2081
		$allgood = true;
2082
		// Is there actually anything that needs our attention?
2083
		foreach ($upcontext['packages'] as $package)
0 ignored issues
show
Bug introduced by
The expression $upcontext['packages'] of type string|boolean|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

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

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

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

Loading history...
2084
			if ($package['color'] != 'green')
2085
				$allgood = false;
2086
2087
		if ($allgood)
2088
			return true;
2089
	}
2090
2091
	$_GET['substep'] = 0;
2092
	return isset($_POST['cleandone']) ? true : false;
2093
}
2094
2095
2096
// Delete the damn thing!
2097
function DeleteUpgrade()
2098
{
2099
	global $command_line, $language, $upcontext, $boarddir, $sourcedir, $forum_version, $user_info, $maintenance, $smcFunc, $db_type;
2100
2101
	// Now it's nice to have some of the basic SMF source files.
2102
	if (!isset($_GET['ssi']) && !$command_line)
2103
		redirectLocation('&ssi=1');
2104
2105
	$upcontext['sub_template'] = 'upgrade_complete';
2106
	$upcontext['page_title'] = 'Upgrade Complete';
2107
2108
	$endl = $command_line ? "\n" : '<br>' . "\n";
2109
2110
	$changes = array(
2111
		'language' => '\'' . (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language) . '\'',
2112
		'db_error_send' => '1',
2113
		'upgradeData' => '#remove#',
2114
	);
2115
2116
	// Are we in maintenance mode?
2117
	if (isset($upcontext['user']['main']))
2118
	{
2119
		if ($command_line)
2120
			echo ' * ';
2121
		$upcontext['removed_maintenance'] = true;
2122
		$changes['maintenance'] = $upcontext['user']['main'];
2123
	}
2124
	// Otherwise if somehow we are in 2 let's go to 1.
2125
	elseif (!empty($maintenance) && $maintenance == 2)
2126
		$changes['maintenance'] = 1;
2127
2128
	// Wipe this out...
2129
	$upcontext['user'] = array();
2130
2131
	// Make a backup of Settings.php first as otherwise earlier changes are lost.
2132
	copy($boarddir . '/Settings.php', $boarddir . '/Settings_bak.php');
2133
	changeSettings($changes);
2134
2135
	// Clean any old cache files away.
2136
	clean_cache();
2137
2138
	// Can we delete the file?
2139
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
2140
2141
	// Now is the perfect time to fetch the SM files.
2142
	if ($command_line)
2143
		cli_scheduled_fetchSMfiles();
2144
	else
2145
	{
2146
		require_once($sourcedir . '/ScheduledTasks.php');
2147
		$forum_version = SMF_VERSION;  // The variable is usually defined in index.php so lets just use the constant to do it for us.
2148
		scheduled_fetchSMfiles(); // Now go get those files!
2149
	}
2150
2151
	// Log what we've done.
2152
	if (empty($user_info['id']))
2153
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
2154
2155
	// Log the action manually, so CLI still works.
2156
	$smcFunc['db_insert']('',
2157
		'{db_prefix}log_actions',
2158
		array(
2159
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'string-16', 'action' => 'string',
2160
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
2161
		),
2162
		array(
2163
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
2164
			0, 0, 0, safe_serialize(array('version' => $forum_version, 'member' => $user_info['id'])),
2165
		),
2166
		array('id_action')
2167
	);
2168
	$user_info['id'] = 0;
2169
2170
	// Save the current database version.
2171
	$server_version = $smcFunc['db_server_info']();
2172 View Code Duplication
	if (($db_type == 'mysql' || $db_type == 'mysqli') && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51')))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2173
		updateSettings(array('db_mysql_group_by_fix' => '1'));
2174
2175
	if ($command_line)
2176
	{
2177
		echo $endl;
2178
		echo 'Upgrade Complete!', $endl;
2179
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
2180
		exit;
2181
	}
2182
2183
	// Make sure it says we're done.
2184
	$upcontext['overall_percent'] = 100;
2185
	if (isset($upcontext['step_progress']))
2186
		unset($upcontext['step_progress']);
2187
2188
	$_GET['substep'] = 0;
2189
	return false;
2190
}
2191
2192
// Just like the built in one, but setup for CLI to not use themes.
2193
function cli_scheduled_fetchSMfiles()
2194
{
2195
	global $sourcedir, $language, $forum_version, $modSettings, $smcFunc;
2196
2197
	if (empty($modSettings['time_format']))
2198
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
2199
2200
	// What files do we want to get
2201
	$request = $smcFunc['db_query']('', '
2202
		SELECT id_file, filename, path, parameters
2203
		FROM {db_prefix}admin_info_files',
2204
		array(
2205
		)
2206
	);
2207
2208
	$js_files = array();
2209 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2210
	{
2211
		$js_files[$row['id_file']] = array(
2212
			'filename' => $row['filename'],
2213
			'path' => $row['path'],
2214
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode($forum_version)),
2215
		);
2216
	}
2217
	$smcFunc['db_free_result']($request);
2218
2219
	// We're gonna need fetch_web_data() to pull this off.
2220
	require_once($sourcedir . '/Subs-Package.php');
2221
2222
	foreach ($js_files as $ID_FILE => $file)
2223
	{
2224
		// Create the url
2225
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'http://www.simplemachines.org' : '';
2226
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
2227
2228
		// Get the file
2229
		$file_data = fetch_web_data($url);
2230
2231
		// If we got an error - give up - the site might be down.
2232
		if ($file_data === false)
2233
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
2234
2235
		// Save the file to the database.
2236
		$smcFunc['db_query']('substring', '
2237
			UPDATE {db_prefix}admin_info_files
2238
			SET data = SUBSTRING({string:file_data}, 1, 65534)
2239
			WHERE id_file = {int:id_file}',
2240
			array(
2241
				'id_file' => $ID_FILE,
2242
				'file_data' => $file_data,
2243
			)
2244
		);
2245
	}
2246
	return true;
2247
}
2248
2249
function convertSettingsToTheme()
2250
{
2251
	global $db_prefix, $modSettings, $smcFunc;
2252
2253
	$values = array(
2254
		'show_latest_member' => @$GLOBALS['showlatestmember'],
2255
		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
2256
		'show_modify' => @$GLOBALS['showmodify'],
2257
		'show_user_images' => @$GLOBALS['showuserpic'],
2258
		'show_blurb' => @$GLOBALS['showusertext'],
2259
		'show_gender' => @$GLOBALS['showgenderimage'],
2260
		'show_newsfader' => @$GLOBALS['shownewsfader'],
2261
		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
2262
		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
2263
		'linktree_link' => @$GLOBALS['curposlinks'],
2264
		'show_profile_buttons' => @$GLOBALS['profilebutton'],
2265
		'show_mark_read' => @$GLOBALS['showmarkread'],
2266
		'newsfader_time' => @$GLOBALS['fadertime'],
2267
		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
2268
		'enable_news' => @$GLOBALS['enable_news'],
2269
		'return_to_post' => @$modSettings['returnToPost'],
2270
	);
2271
2272
	$themeData = array();
2273
	foreach ($values as $variable => $value)
2274
	{
2275
		if (!isset($value) || $value === null)
2276
			$value = 0;
2277
2278
		$themeData[] = array(0, 1, $variable, $value);
2279
	}
2280
	if (!empty($themeData))
2281
	{
2282
		$smcFunc['db_insert']('ignore',
2283
			$db_prefix . 'themes',
2284
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
2285
			$themeData,
2286
			array('id_member', 'id_theme', 'variable')
2287
		);
2288
	}
2289
}
2290
2291
// This function only works with MySQL but that's fine as it is only used for v1.0.
2292
function convertSettingstoOptions()
2293
{
2294
	global $modSettings, $smcFunc;
2295
2296
	// Format: new_setting -> old_setting_name.
2297
	$values = array(
2298
		'calendar_start_day' => 'cal_startmonday',
2299
		'view_newest_first' => 'viewNewestFirst',
2300
		'view_newest_pm_first' => 'viewNewestFirst',
2301
	);
2302
2303
	foreach ($values as $variable => $value)
2304
	{
2305
		if (empty($modSettings[$value[0]]))
2306
			continue;
2307
2308
		$smcFunc['db_query']('', '
2309
			INSERT IGNORE INTO {db_prefix}themes
2310
				(id_member, id_theme, variable, value)
2311
			SELECT id_member, 1, {string:variable}, {string:value}
2312
			FROM {db_prefix}members',
2313
			array(
2314
				'variable' => $variable,
2315
				'value' => $modSettings[$value[0]],
2316
				'db_error_skip' => true,
2317
			)
2318
		);
2319
2320
		$smcFunc['db_query']('', '
2321
			INSERT IGNORE INTO {db_prefix}themes
2322
				(id_member, id_theme, variable, value)
2323
			VALUES (-1, 1, {string:variable}, {string:value})',
2324
			array(
2325
				'variable' => $variable,
2326
				'value' => $modSettings[$value[0]],
2327
				'db_error_skip' => true,
2328
			)
2329
		);
2330
	}
2331
}
2332
2333
function changeSettings($config_vars)
2334
{
2335
	global $boarddir;
2336
2337
	$settingsArray = file($boarddir . '/Settings_bak.php');
2338
2339
	if (count($settingsArray) == 1)
2340
		$settingsArray = preg_split('~[\r\n]~', $settingsArray[0]);
2341
2342
	for ($i = 0, $n = count($settingsArray); $i < $n; $i++)
2343
	{
2344
		// Don't trim or bother with it if it's not a variable.
2345
		if (substr($settingsArray[$i], 0, 1) == '$')
2346
		{
2347
			$settingsArray[$i] = trim($settingsArray[$i]) . "\n";
2348
2349
			foreach ($config_vars as $var => $val)
2350
			{
2351
				if (isset($settingsArray[$i]) && strncasecmp($settingsArray[$i], '$' . $var, 1 + strlen($var)) == 0)
2352
				{
2353 View Code Duplication
					if ($val == '#remove#')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2354
						unset($settingsArray[$i]);
2355
					else
2356
					{
2357
						$comment = strstr(substr($settingsArray[$i], strpos($settingsArray[$i], ';')), '#');
2358
						$settingsArray[$i] = '$' . $var . ' = ' . $val . ';' . ($comment != '' ? "\t\t" . $comment : "\n");
2359
					}
2360
2361
					unset($config_vars[$var]);
2362
				}
2363
			}
2364
		}
2365 View Code Duplication
		if (isset($settingsArray[$i]))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2366
		{
2367
			if (trim(substr($settingsArray[$i], 0, 2)) == '?' . '>')
2368
				$end = $i;
2369
		}
2370
	}
2371
2372
	// Assume end-of-file if the end wasn't found.
2373
	if (empty($end) || $end < 10)
2374
		$end = count($settingsArray);
2375
2376 View Code Duplication
	if (!empty($config_vars))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2377
	{
2378
		$settingsArray[$end++] = '';
2379
		foreach ($config_vars as $var => $val)
2380
		{
2381
			if ($val != '#remove#')
2382
				$settingsArray[$end++] = '$' . $var . ' = ' . $val . ';' . "\n";
2383
		}
2384
	}
2385
	// This should be the last line and even last bytes of the file.
2386
	$settingsArray[$end] = '?' . '>';
2387
2388
	// Blank out the file - done to fix a oddity with some servers.
2389
	$fp = fopen($boarddir . '/Settings.php', 'w');
2390
	fclose($fp);
2391
2392
	$fp = fopen($boarddir . '/Settings.php', 'r+');
2393
	for ($i = 0; $i < $end; $i++)
2394
	{
2395
		if (isset($settingsArray[$i]))
2396
			fwrite($fp, strtr($settingsArray[$i], "\r", ''));
2397
	}
2398
	fwrite($fp, rtrim($settingsArray[$i]));
2399
	fclose($fp);
2400
}
2401
function updateLastError()
2402
{
2403
	// clear out the db_last_error file
2404
	file_put_contents(dirname(__FILE__) . '/db_last_error.php', '<' . '?' . "php\n" . '$db_last_error = 0;' . "\n" . '?' . '>');
2405
}
2406
2407
function php_version_check()
2408
{
2409
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
2410
}
2411
2412
function db_version_check()
2413
{
2414
	global $db_type, $databases;
2415
2416
	$curver = eval($databases[$db_type]['version_check']);
2417
	$curver = preg_replace('~\-.+?$~', '', $curver);
2418
2419
	return version_compare($databases[$db_type]['version'], $curver, '<=');
2420
}
2421
2422
function getMemberGroups()
2423
{
2424
	global $smcFunc;
2425
	static $member_groups = array();
2426
2427
	if (!empty($member_groups))
2428
		return $member_groups;
2429
2430
	$request = $smcFunc['db_query']('', '
2431
		SELECT group_name, id_group
2432
		FROM {db_prefix}membergroups
2433
		WHERE id_group = {int:admin_group} OR id_group > {int:old_group}',
2434
		array(
2435
			'admin_group' => 1,
2436
			'old_group' => 7,
2437
			'db_error_skip' => true,
2438
		)
2439
	);
2440
	if ($request === false)
2441
	{
2442
		$request = $smcFunc['db_query']('', '
2443
			SELECT membergroup, id_group
2444
			FROM {db_prefix}membergroups
2445
			WHERE id_group = {int:admin_group} OR id_group > {int:old_group}',
2446
			array(
2447
				'admin_group' => 1,
2448
				'old_group' => 7,
2449
				'db_error_skip' => true,
2450
			)
2451
		);
2452
	}
2453
	while ($row = $smcFunc['db_fetch_row']($request))
2454
		$member_groups[trim($row[0])] = $row[1];
2455
	$smcFunc['db_free_result']($request);
2456
2457
	return $member_groups;
2458
}
2459
2460
function fixRelativePath($path)
2461
{
2462
	global $install_path;
2463
2464
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
2465
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
2466
}
2467
2468
function parse_sql($filename)
2469
{
2470
	global $db_prefix, $db_collation, $boarddir, $boardurl, $command_line, $file_steps, $step_progress, $custom_warning;
2471
	global $upcontext, $support_js, $is_debug, $smcFunc, $databases, $db_type, $db_character_set;
2472
2473
/*
2474
	Failure allowed on:
2475
		- INSERT INTO but not INSERT IGNORE INTO.
2476
		- UPDATE IGNORE but not UPDATE.
2477
		- ALTER TABLE and ALTER IGNORE TABLE.
2478
		- DROP TABLE.
2479
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
2480
2481
	If a comment...
2482
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
2483
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
2484
		- is only ---#, it is "done." and then a break - only shown in debug.
2485
		- begins with ---{ it is a code block terminating at ---}.
2486
2487
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
2488
2489
	Replaces the following variables:
2490
		- {$boarddir}
2491
		- {$boardurl}
2492
		- {$db_prefix}
2493
		- {$db_collation}
2494
*/
2495
2496
	// May want to use extended functionality.
2497
	db_extend();
2498
	db_extend('packages');
2499
2500
	// Our custom error handler - does nothing but does stop public errors from XML!
2501
	if (!function_exists('sql_error_handler'))
2502
	{
2503
		function sql_error_handler($errno, $errstr, $errfile, $errline)
0 ignored issues
show
Unused Code introduced by
The parameter $errno is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2504
		{
2505
			global $support_js;
2506
2507
			if ($support_js)
2508
				return true;
2509
			else
2510
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
2511
		}
2512
	}
2513
2514
	// Make our own error handler.
2515
	set_error_handler('sql_error_handler');
2516
2517
	// 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!
2518
	if (!empty($databases[$db_type]['utf8_support']) && version_compare($databases[$db_type]['utf8_version'], eval($databases[$db_type]['utf8_version_check']), '>'))
2519
	{
2520
		$request = $smcFunc['db_query']('', '
2521
			SHOW TABLE STATUS
2522
			LIKE {string:table_name}',
2523
			array(
2524
				'table_name' => "{$db_prefix}members",
2525
				'db_error_skip' => true,
2526
			)
2527
		);
2528
		if ($smcFunc['db_num_rows']($request) === 0)
2529
			die('Unable to find members table!');
2530
		$table_status = $smcFunc['db_fetch_assoc']($request);
2531
		$smcFunc['db_free_result']($request);
2532
2533
		if (!empty($table_status['Collation']))
2534
		{
2535
			$request = $smcFunc['db_query']('', '
2536
				SHOW COLLATION
2537
				LIKE {string:collation}',
2538
				array(
2539
					'collation' => $table_status['Collation'],
2540
					'db_error_skip' => true,
2541
				)
2542
			);
2543
			// Got something?
2544
			if ($smcFunc['db_num_rows']($request) !== 0)
2545
				$collation_info = $smcFunc['db_fetch_assoc']($request);
2546
			$smcFunc['db_free_result']($request);
2547
2548
			// Excellent!
2549
			if (!empty($collation_info['Collation']) && !empty($collation_info['Charset']))
2550
				$db_collation = ' CHARACTER SET ' . $collation_info['Charset'] . ' COLLATE ' . $collation_info['Collation'];
2551
		}
2552
	}
2553
	if (empty($db_collation))
2554
		$db_collation = '';
2555
2556
	$endl = $command_line ? "\n" : '<br>' . "\n";
2557
2558
	$lines = file($filename);
2559
2560
	$current_type = 'sql';
2561
	$current_data = '';
2562
	$substep = 0;
2563
	$last_step = '';
2564
2565
	// Make sure all newly created tables will have the proper characters set.
2566
	if (isset($db_character_set) && $db_character_set === 'utf8')
2567
		$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
2568
2569
	// Count the total number of steps within this file - for progress.
2570
	$file_steps = substr_count(implode('', $lines), '---#');
2571
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
2572
	$upcontext['debug_items'] = $file_steps;
2573
	$upcontext['current_item_num'] = 0;
2574
	$upcontext['current_item_name'] = '';
2575
	$upcontext['current_debug_item_num'] = 0;
2576
	$upcontext['current_debug_item_name'] = '';
2577
	// This array keeps a record of what we've done in case java is dead...
2578
	$upcontext['actioned_items'] = array();
2579
2580
	$done_something = false;
2581
2582
	foreach ($lines as $line_number => $line)
2583
	{
2584
		$do_current = $substep >= $_GET['substep'];
2585
2586
		// Get rid of any comments in the beginning of the line...
2587
		if (substr(trim($line), 0, 2) === '/*')
2588
			$line = preg_replace('~/\*.+?\*/~', '', $line);
2589
2590
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
2591
		if ($is_debug && !$support_js && $command_line)
2592
			flush();
2593
2594
		if (trim($line) === '')
2595
			continue;
2596
2597
		if (trim(substr($line, 0, 3)) === '---')
2598
		{
2599
			$type = substr($line, 3, 1);
2600
2601
			// An error??
2602
			if (trim($current_data) != '' && $type !== '}')
2603
			{
2604
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
2605
				if ($command_line)
2606
					echo $upcontext['error_message'];
2607
			}
2608
2609
			if ($type == ' ')
2610
			{
2611
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
2612
				{
2613
					echo ' Successful.', $endl;
2614
					flush();
2615
				}
2616
2617
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
2618
				$upcontext['current_item_num']++;
2619
				$upcontext['current_item_name'] = $last_step;
2620
2621
				if ($do_current)
2622
				{
2623
					$upcontext['actioned_items'][] = $last_step;
2624
					if ($command_line)
2625
						echo ' * ';
2626
				}
2627
			}
2628
			elseif ($type == '#')
2629
			{
2630
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
2631
2632
				$upcontext['current_debug_item_num']++;
2633
				if (trim($line) != '---#')
2634
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
2635
2636
				// Have we already done something?
2637
				if (isset($_GET['xml']) && $done_something)
2638
				{
2639
					restore_error_handler();
2640
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
2641
				}
2642
2643
				if ($do_current)
2644
				{
2645
					if (trim($line) == '---#' && $command_line)
2646
						echo ' done.', $endl;
2647
					elseif ($command_line)
2648
						echo ' +++ ', rtrim(substr($line, 4));
2649
					elseif (trim($line) != '---#')
2650
					{
2651
						if ($is_debug)
2652
							$upcontext['actioned_items'][] = htmlspecialchars(rtrim(substr($line, 4)));
2653
					}
2654
				}
2655
2656
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
2657
				{
2658
					if ($command_line)
2659
						echo ' * ';
2660
					else
2661
						$upcontext['actioned_items'][] = $last_step;
2662
				}
2663
2664
				// Small step - only if we're actually doing stuff.
2665
				if ($do_current)
2666
					nextSubstep(++$substep);
2667
				else
2668
					$substep++;
2669
			}
2670
			elseif ($type == '{')
2671
				$current_type = 'code';
2672
			elseif ($type == '}')
2673
			{
2674
				$current_type = 'sql';
2675
2676
				if (!$do_current)
2677
				{
2678
					$current_data = '';
2679
					continue;
2680
				}
2681
2682
				if (eval('global $db_prefix, $modSettings, $smcFunc; ' . $current_data) === false)
2683
				{
2684
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
2685
					if ($command_line)
2686
						echo $upcontext['error_message'];
2687
				}
2688
2689
				// Done with code!
2690
				$current_data = '';
2691
				$done_something = true;
2692
			}
2693
2694
			continue;
2695
		}
2696
2697
		$current_data .= $line;
2698
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
2699
		{
2700
			if ((!$support_js || isset($_GET['xml'])))
2701
			{
2702
				if (!$do_current)
2703
				{
2704
					$current_data = '';
2705
					continue;
2706
				}
2707
2708
				$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));
2709
2710
				upgrade_query($current_data);
2711
2712
				// @todo This will be how it kinda does it once mysql all stripped out - needed for postgre (etc).
2713
				/*
2714
				$result = $smcFunc['db_query']('', $current_data, false, false);
2715
				// Went wrong?
2716
				if (!$result)
2717
				{
2718
					// Bit of a bodge - do we want the error?
2719
					if (!empty($upcontext['return_error']))
2720
					{
2721
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
2722
						return false;
2723
					}
2724
				}*/
2725
				$done_something = true;
2726
			}
2727
			$current_data = '';
2728
		}
2729
		// If this is xml based and we're just getting the item name then that's grand.
2730
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
2731
		{
2732
			restore_error_handler();
2733
			return false;
2734
		}
2735
2736
		// Clean up by cleaning any step info.
2737
		$step_progress = array();
2738
		$custom_warning = '';
2739
	}
2740
2741
	// Put back the error handler.
2742
	restore_error_handler();
2743
2744
	if ($command_line)
2745
	{
2746
		echo ' Successful.' . "\n";
2747
		flush();
2748
	}
2749
2750
	$_GET['substep'] = 0;
2751
	return true;
2752
}
2753
2754
function upgrade_query($string, $unbuffered = false)
2755
{
2756
	global $db_connection, $db_server, $db_user, $db_passwd, $db_type, $command_line, $upcontext, $upgradeurl, $modSettings;
2757
	global $db_name, $db_unbuffered, $smcFunc;
2758
2759
	// Get the query result - working around some SMF specific security - just this once!
2760
	$modSettings['disableQueryCheck'] = true;
2761
	$db_unbuffered = $unbuffered;
2762
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
2763
	$db_unbuffered = false;
2764
2765
	// Failure?!
2766
	if ($result !== false)
2767
		return $result;
2768
2769
	$db_error_message = $smcFunc['db_error']($db_connection);
2770
	// If MySQL we do something more clever.
2771
	if ($db_type == 'mysql' || $db_type == 'mysqli')
2772
	{
2773
		$mysql_errno = ($db_type == 'mysqli') ? mysqli_errno($db_connection) : mysql_errno($db_connection);
2774
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR'));
2775
2776
		// Error numbers:
2777
		//    1016: Can't open file '....MYI'
2778
		//    1050: Table already exists.
2779
		//    1054: Unknown column name.
2780
		//    1060: Duplicate column name.
2781
		//    1061: Duplicate key name.
2782
		//    1062: Duplicate entry for unique key.
2783
		//    1068: Multiple primary keys.
2784
		//    1072: Key column '%s' doesn't exist in table.
2785
		//    1091: Can't drop key, doesn't exist.
2786
		//    1146: Table doesn't exist.
2787
		//    2013: Lost connection to server during query.
2788
2789
		if ($mysql_errno == 1016)
2790
		{
2791
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
2792
			{
2793
				if ($db_type == 'mysql')
2794
				{
2795
					mysql_query('REPAIR TABLE `' . $match[1] . '`');
2796
					$result = mysql_query($string);
2797
				}
2798
				else
2799
				{
2800
					mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
2801
					$result = mysqli_query($db_connection, $string);
2802
				}
2803
				if ($result !== false)
2804
					return $result;
2805
			}
2806
		}
2807
		elseif ($mysql_errno == 2013)
2808
		{
2809
			$db_connection = mysql_connect($db_server, $db_user, $db_passwd);
2810
			if ($db_type == 'mysql')
2811
			{
2812
				mysql_select_db($db_name, $db_connection);
2813
				if ($db_connection)
2814
				{
2815
					$result = mysql_query($string);
2816
					if ($result !== false)
2817
						return $result;
2818
				}
2819
			}
2820
			else
2821
			{
2822
				mysqli_select_db($db_connection, $db_name);
2823
				if ($db_connection)
2824
				{
2825
					$result = mysqli_query($db_connection, $string);
2826
					if ($result !== false)
2827
						return $result;
2828
				}
2829
			}
2830
		}
2831
		// Duplicate column name... should be okay ;).
2832 View Code Duplication
		elseif (in_array($mysql_errno, array(1060, 1061, 1068, 1091)))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2833
			return false;
2834
		// Duplicate insert... make sure it's the proper type of query ;).
2835 View Code Duplication
		elseif (in_array($mysql_errno, array(1054, 1062, 1146)) && $error_query)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2836
			return false;
2837
		// Creating an index on a non-existent column.
2838
		elseif ($mysql_errno == 1072)
2839
			return false;
2840
		elseif ($mysql_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
2841
			return false;
2842
	}
2843
	// If a table already exists don't go potty.
2844
	else
2845
	{
2846
		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U')))
2847
		{
2848
			if (strpos($db_error_message, 'exist') !== false)
2849
				return true;
2850
		}
2851
		elseif (strpos(trim($string), 'INSERT ') !== false)
2852
		{
2853
			if (strpos($db_error_message, 'duplicate') !== false)
2854
				return true;
2855
		}
2856
	}
2857
2858
	// Get the query string so we pass everything.
2859
	$query_string = '';
2860
	foreach ($_GET as $k => $v)
2861
		$query_string .= ';' . $k . '=' . $v;
2862
	if (strlen($query_string) != 0)
2863
		$query_string = '?' . substr($query_string, 1);
2864
2865
	if ($command_line)
2866
	{
2867
		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
2868
		die;
2869
	}
2870
2871
	// Bit of a bodge - do we want the error?
2872
	if (!empty($upcontext['return_error']))
2873
	{
2874
		$upcontext['error_message'] = $db_error_message;
2875
		$upcontext['error_string'] = $string;
2876
		return false;
2877
	}
2878
2879
	// Otherwise we have to display this somewhere appropriate if possible.
2880
	$upcontext['forced_error_message'] = '
2881
			<strong>Unsuccessful!</strong><br>
2882
2883
			<div style="margin: 2ex;">
2884
				This query:
2885
				<blockquote><pre>' . nl2br(htmlspecialchars(trim($string))) . ';</pre></blockquote>
2886
2887
				Caused the error:
2888
				<blockquote>' . nl2br(htmlspecialchars($db_error_message)) . '</blockquote>
2889
			</div>
2890
2891
			<form action="' . $upgradeurl . $query_string . '" method="post">
2892
				<input type="submit" value="Try again" class="button_submit">
2893
			</form>
2894
		</div>';
2895
2896
	upgradeExit();
2897
}
2898
2899
function smf_mysql_fetch_assoc($rs)
2900
{
2901
	global $db_type;
2902
	return ($db_type == 'mysql') ? mysql_fetch_assoc($rs) : mysqli_fetch_assoc($rs);
2903
}
2904
2905
function smf_mysql_fetch_row($rs)
2906
{
2907
	global $db_type;
2908
	return ($db_type == 'mysql') ? mysql_fetch_row($rs) : mysqli_fetch_row($rs);
2909
}
2910
2911
function smf_mysql_free_result($rs)
2912
{
2913
	global $db_type;
2914
	return ($db_type == 'mysql') ? mysql_free_result($rs) : mysqli_free_result($rs);
2915
}
2916
2917
function smf_mysql_insert_id($rs)
2918
{
2919
	global $db_type;
2920
	return ($db_type == 'mysql') ? mysql_insert_id($rs) : mysqli_insert_id($rs);
2921
}
2922
2923
function smf_mysql_num_rows($rs)
2924
{
2925
	global $db_type;
2926
	return ($db_type == 'mysql') ? mysql_num_rows($rs) : mysqli_num_rows($rs);
2927
}
2928
2929
function smf_mysql_real_escape_string($string)
2930
{
2931
	global $db_type, $db_connection;
2932
	return ($db_type == 'mysql') ? mysql_real_escape_string($string, $db_connection) : mysqli_real_escape_string($db_connection, $string);
2933
}
2934
2935
// This performs a table alter, but does it unbuffered so the script can time out professionally.
2936
function protected_alter($change, $substep, $is_test = false)
2937
{
2938
	global $db_prefix, $smcFunc;
2939
2940
	db_extend('packages');
2941
2942
	// Firstly, check whether the current index/column exists.
2943
	$found = false;
2944
	if ($change['type'] === 'column')
2945
	{
2946
		$columns = $smcFunc['db_list_columns']('{db_prefix}' . $change['table'], true);
2947
		foreach ($columns as $column)
2948
		{
2949
			// Found it?
2950
			if ($column['name'] === $change['name'])
2951
			{
2952
				$found |= 1;
2953
				// Do some checks on the data if we have it set.
2954
				if (isset($change['col_type']))
2955
					$found &= $change['col_type'] === $column['type'];
2956
				if (isset($change['null_allowed']))
2957
					$found &= $column['null'] == $change['null_allowed'];
2958
				if (isset($change['default']))
2959
					$found &= $change['default'] === $column['default'];
2960
			}
2961
		}
2962
	}
2963
	elseif ($change['type'] === 'index')
2964
	{
2965
		$request = upgrade_query( '
2966
			SHOW INDEX
2967
			FROM ' . $db_prefix . $change['table']);
2968
		if ($request !== false)
2969
		{
2970
			$cur_index = array();
2971
2972
			while ($row = $smcFunc['db_fetch_assoc']($request))
2973
				if ($row['Key_name'] === $change['name'])
2974
					$cur_index[(int) $row['Seq_in_index']] = $row['Column_name'];
2975
2976
			ksort($cur_index, SORT_NUMERIC);
2977
			$found = array_values($cur_index) === $change['target_columns'];
2978
2979
			$smcFunc['db_free_result']($request);
2980
		}
2981
	}
2982
2983
	// If we're trying to add and it's added, we're done.
2984
	if ($found && in_array($change['method'], array('add', 'change')))
2985
		return true;
2986
	// Otherwise if we're removing and it wasn't found we're also done.
2987
	elseif (!$found && in_array($change['method'], array('remove', 'change_remove')))
2988
		return true;
2989
	// Otherwise is it just a test?
2990
	elseif ($is_test)
2991
		return false;
2992
2993
	// Not found it yet? Bummer! How about we see if we're currently doing it?
2994
	$running = false;
2995
	$found = false;
2996
	while (1 == 1)
2997
	{
2998
		$request = upgrade_query('
2999
			SHOW FULL PROCESSLIST');
3000
		while ($row = $smcFunc['db_fetch_assoc']($request))
3001
		{
3002
			if (strpos($row['Info'], 'ALTER TABLE ' . $db_prefix . $change['table']) !== false && strpos($row['Info'], $change['text']) !== false)
3003
				$found = true;
3004
		}
3005
3006
		// Can't find it? Then we need to run it fools!
3007
		if (!$found && !$running)
3008
		{
3009
			$smcFunc['db_free_result']($request);
3010
3011
			$success = upgrade_query('
3012
				ALTER TABLE ' . $db_prefix . $change['table'] . '
3013
				' . $change['text'], true) !== false;
3014
3015
			if (!$success)
3016
				return false;
3017
3018
			// Return
3019
			$running = true;
3020
		}
3021
		// What if we've not found it, but we'd ran it already? Must of completed.
3022
		elseif (!$found)
3023
		{
3024
			$smcFunc['db_free_result']($request);
3025
			return true;
3026
		}
3027
3028
		// Pause execution for a sec or three.
3029
		sleep(3);
3030
3031
		// Can never be too well protected.
3032
		nextSubstep($substep);
3033
	}
3034
3035
	// Protect it.
3036
	nextSubstep($substep);
3037
}
3038
3039
// Alter a text column definition preserving its character set.
3040
function textfield_alter($change, $substep)
3041
{
3042
	global $db_prefix, $databases, $db_type, $smcFunc;
3043
3044
	// Versions of MySQL < 4.1 wouldn't benefit from character set detection.
3045
	if (empty($databases[$db_type]['utf8_support']) || version_compare($databases[$db_type]['utf8_version'], eval($databases[$db_type]['utf8_version_check']), '>'))
3046
	{
3047
		$column_fix = true;
3048
		$null_fix = !$change['null_allowed'];
3049
	}
3050
	else
3051
	{
3052
		$request = $smcFunc['db_query']('', '
3053
			SHOW FULL COLUMNS
3054
			FROM {db_prefix}' . $change['table'] . '
3055
			LIKE {string:column}',
3056
			array(
3057
				'column' => $change['column'],
3058
				'db_error_skip' => true,
3059
			)
3060
		);
3061
		if ($smcFunc['db_num_rows']($request) === 0)
3062
			die('Unable to find column ' . $change['column'] . ' inside table ' . $db_prefix . $change['table']);
3063
		$table_row = $smcFunc['db_fetch_assoc']($request);
3064
		$smcFunc['db_free_result']($request);
3065
3066
		// If something of the current column definition is different, fix it.
3067
		$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']);
3068
3069
		// Columns that previously allowed null, need to be converted first.
3070
		$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
3071
3072
		// Get the character set that goes with the collation of the column.
3073 View Code Duplication
		if ($column_fix && !empty($table_row['Collation']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
3074
		{
3075
			$request = $smcFunc['db_query']('', '
3076
				SHOW COLLATION
3077
				LIKE {string:collation}',
3078
				array(
3079
					'collation' => $table_row['Collation'],
3080
					'db_error_skip' => true,
3081
				)
3082
			);
3083
			// No results? Just forget it all together.
3084
			if ($smcFunc['db_num_rows']($request) === 0)
3085
				unset($table_row['Collation']);
3086
			else
3087
				$collation_info = $smcFunc['db_fetch_assoc']($request);
3088
			$smcFunc['db_free_result']($request);
3089
		}
3090
	}
3091
3092
	if ($column_fix)
3093
	{
3094
		// Make sure there are no NULL's left.
3095
		if ($null_fix)
3096
			$smcFunc['db_query']('', '
3097
				UPDATE {db_prefix}' . $change['table'] . '
3098
				SET ' . $change['column'] . ' = {string:default}
3099
				WHERE ' . $change['column'] . ' IS NULL',
3100
				array(
3101
					'default' => isset($change['default']) ? $change['default'] : '',
3102
					'db_error_skip' => true,
3103
				)
3104
			);
3105
3106
		// Do the actual alteration.
3107
		$smcFunc['db_query']('', '
3108
			ALTER TABLE {db_prefix}' . $change['table'] . '
3109
			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}' : ''),
3110
			array(
3111
				'default' => isset($change['default']) ? $change['default'] : '',
3112
				'db_error_skip' => true,
3113
			)
3114
		);
3115
	}
3116
	nextSubstep($substep);
3117
}
3118
3119
// Check if we need to alter this query.
3120
function checkChange(&$change)
3121
{
3122
	global $smcFunc, $db_type, $databases;
3123
	static $database_version, $where_field_support;
3124
3125
	// Attempt to find a database_version.
3126
	if (empty($database_version))
3127
	{
3128
		$database_version = $databases[$db_type]['version_check'];
3129
		$where_field_support = ($db_type == 'mysql' || $db_type == 'mysqli') && version_compare('5.0', $database_version, '<=');
3130
	}
3131
3132
	// Not a column we need to check on?
3133
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
3134
		return;
3135
3136
	// Break it up you (six|seven).
3137
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
3138
3139
	// Can we support a shortcut method?
3140
	if ($where_field_support)
3141
	{
3142
		// Get the details about this change.
3143
		$request = $smcFunc['db_query']('', '
3144
			SHOW FIELDS
3145
			FROM {db_prefix}{raw:table}
3146
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
3147
			array(
3148
				'table' => $change['table'],
3149
				'old_name' => $temp[1],
3150
				'new_name' => $temp[2],
3151
		));
3152
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
3153
		if ($smcFunc['db_num_rows'] != 1)
3154
			return;
3155
3156
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
3157
		$smcFunc['db_free_result']($request);
3158
	}
3159
	else
3160
	{
3161
		// Do this the old fashion, sure method way.
3162
		$request = $smcFunc['db_query']('', '
3163
			SHOW FIELDS
3164
			FROM {db_prefix}{raw:table}',
3165
			array(
3166
				'table' => $change['table'],
3167
		));
3168
		// Mayday!
3169
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
3170
		if ($smcFunc['db_num_rows'] == 0)
3171
			return;
3172
3173
		// Oh where, oh where has my little field gone. Oh where can it be...
3174
		while ($row = $smcFunc['db_query']($request))
3175
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
3176
			{
3177
				$current_type = $row['Type'];
3178
				break;
3179
			}
3180
	}
3181
3182
	// If this doesn't match, the column may of been altered for a reason.
3183
	if (trim($current_type) != trim($temp[3]))
3184
		$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...
3185
3186
	// Piece this back together.
3187
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
3188
}
3189
3190
// The next substep.
3191
function nextSubstep($substep)
3192
{
3193
	global $start_time, $timeLimitThreshold, $command_line, $custom_warning;
3194
	global $step_progress, $is_debug, $upcontext;
3195
3196
	if ($_GET['substep'] < $substep)
3197
		$_GET['substep'] = $substep;
3198
3199
	if ($command_line)
3200
	{
3201
		if (time() - $start_time > 1 && empty($is_debug))
3202
		{
3203
			echo '.';
3204
			$start_time = time();
3205
		}
3206
		return;
3207
	}
3208
3209
	@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...
3210
	if (function_exists('apache_reset_timeout'))
3211
		@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...
3212
3213
	if (time() - $start_time <= $timeLimitThreshold)
3214
		return;
3215
3216
	// Do we have some custom step progress stuff?
3217
	if (!empty($step_progress))
3218
	{
3219
		$upcontext['substep_progress'] = 0;
3220
		$upcontext['substep_progress_name'] = $step_progress['name'];
3221
		if ($step_progress['current'] > $step_progress['total'])
3222
			$upcontext['substep_progress'] = 99.9;
3223
		else
3224
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
3225
3226
		// Make it nicely rounded.
3227
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
3228
	}
3229
3230
	// If this is XML we just exit right away!
3231
	if (isset($_GET['xml']))
3232
		return upgradeExit();
3233
3234
	// We're going to pause after this!
3235
	$upcontext['pause'] = true;
3236
3237
	$upcontext['query_string'] = '';
3238
	foreach ($_GET as $k => $v)
3239
	{
3240
		if ($k != 'data' && $k != 'substep' && $k != 'step')
3241
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
3242
	}
3243
3244
	// Custom warning?
3245
	if (!empty($custom_warning))
3246
		$upcontext['custom_warning'] = $custom_warning;
3247
3248
	upgradeExit();
3249
}
3250
3251
function cmdStep0()
3252
{
3253
	global $boarddir, $sourcedir, $language, $modSettings, $start_time, $cachedir, $databases, $db_type, $smcFunc, $upcontext;
3254
	global $language, $is_debug;
3255
	$start_time = time();
3256
3257
	ob_end_clean();
3258
	ob_implicit_flush(true);
3259
	@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...
3260
3261
	if (!isset($_SERVER['argv']))
3262
		$_SERVER['argv'] = array();
3263
	$_GET['maint'] = 1;
3264
3265
	foreach ($_SERVER['argv'] as $i => $arg)
3266
	{
3267
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
3268
			$_GET['lang'] = $match[1];
3269
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
3270
			continue;
3271
		elseif ($arg == '--no-maintenance')
3272
			$_GET['maint'] = 0;
3273
		elseif ($arg == '--debug')
3274
			$is_debug = true;
3275
		elseif ($arg == '--backup')
3276
			$_POST['backup'] = 1;
3277
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
3278
			$_GET['conv'] = 1;
3279
		elseif ($i != 0)
3280
		{
3281
			echo 'SMF Command-line Upgrader
3282
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
3283
3284
    --language=LANG         Reset the forum\'s language to LANG.
3285
    --no-maintenance        Don\'t put the forum into maintenance mode.
3286
    --debug                 Output debugging information.
3287
    --backup                Create backups of tables with "backup_" prefix.';
3288
			echo "\n";
3289
			exit;
3290
		}
3291
	}
3292
3293
	if (!php_version_check())
3294
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
3295
	if (!db_version_check())
3296
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
3297
3298
	// Do some checks to make sure they have proper privileges
3299
	db_extend('packages');
3300
3301
	// CREATE
3302
	$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');
3303
3304
	// ALTER
3305
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'tinytext', 'null' => false, 'default' => ''));
3306
3307
	// DROP
3308
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
3309
3310
	// Sorry... we need CREATE, ALTER and DROP
3311 View Code Duplication
	if (!$create || !$alter || !$drop)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
3312
		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);
3313
3314
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
3315
		&& @file_exists($sourcedir . '/QueryString.php')
3316
		&& @file_exists($sourcedir . '/ManageBoards.php');
3317
	if (!$check && !isset($modSettings['smfVersion']))
3318
		print_error('Error: Some files are missing or out-of-date.', true);
3319
3320
	// Do a quick version spot check.
3321
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
3322
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
3323
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
3324
		print_error('Error: Some files have not yet been updated properly.');
3325
3326
	// Make sure Settings.php is writable.
3327
		quickFileWritable($boarddir . '/Settings.php');
3328
	if (!is_writable($boarddir . '/Settings.php'))
3329
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
3330
3331
	// Make sure Settings_bak.php is writable.
3332
		quickFileWritable($boarddir . '/Settings_bak.php');
3333
	if (!is_writable($boarddir . '/Settings_bak.php'))
3334
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
3335
3336 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
3337
		print_error('Error: Unable to obtain write access to "agreement.txt".');
3338
	elseif (isset($modSettings['agreement']))
3339
	{
3340
		$fp = fopen($boarddir . '/agreement.txt', 'w');
3341
		fwrite($fp, $modSettings['agreement']);
3342
		fclose($fp);
3343
	}
3344
3345
	// Make sure Themes is writable.
3346
	quickFileWritable($modSettings['theme_dir']);
3347
3348
	if (!is_writable($modSettings['theme_dir']) && !isset($modSettings['smfVersion']))
3349
		print_error('Error: Unable to obtain write access to "Themes".');
3350
3351
	// Make sure cache directory exists and is writable!
3352
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
3353
	if (!file_exists($cachedir_temp))
3354
		@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...
3355
3356
	// Make sure the cache temp dir is writable.
3357
	quickFileWritable($cachedir_temp);
3358
3359
	if (!is_writable($cachedir_temp))
3360
		print_error('Error: Unable to obtain write access to "cache".', true);
3361
3362
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
3363
		print_error('Error: Unable to find language files!', true);
3364
	else
3365
	{
3366
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
3367
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
3368
3369
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
3370
			print_error('Error: Language files out of date.', true);
3371
		if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
3372
			print_error('Error: Install language is missing for selected language.', true);
3373
3374
		// Otherwise include it!
3375
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
3376
	}
3377
3378
	// Make sure we skip the HTML for login.
3379
	$_POST['upcont'] = true;
3380
	$upcontext['current_step'] = 1;
3381
}
3382
3383
function print_error($message, $fatal = false)
3384
{
3385
	static $fp = null;
3386
3387
	if ($fp === null)
3388
		$fp = fopen('php://stderr', 'wb');
3389
3390
	fwrite($fp, $message . "\n");
3391
3392
	if ($fatal)
3393
		exit;
3394
}
3395
3396
function throw_error($message)
3397
{
3398
	global $upcontext;
3399
3400
	$upcontext['error_msg'] = $message;
3401
	$upcontext['sub_template'] = 'error_message';
3402
3403
	return false;
3404
}
3405
3406
// Check files are writable - make them writable if necessary...
3407
function makeFilesWritable(&$files)
3408
{
3409
	global $upcontext, $boarddir;
3410
3411
	if (empty($files))
3412
		return true;
3413
3414
	$failure = false;
3415
	// On linux, it's easy - just use is_writable!
3416
	if (substr(__FILE__, 1, 2) != ':\\')
3417
	{
3418
		$upcontext['systemos'] = 'linux';
3419
3420
		foreach ($files as $k => $file)
3421
		{
3422
			if (!is_writable($file))
3423
			{
3424
				@chmod($file, 0755);
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...
3425
3426
				// Well, 755 hopefully worked... if not, try 777.
3427
				if (!is_writable($file) && !@chmod($file, 0777))
3428
					$failure = true;
3429
				// Otherwise remove it as it's good!
3430
				else
3431
					unset($files[$k]);
3432
			}
3433
			else
3434
				unset($files[$k]);
3435
		}
3436
	}
3437
	// Windows is trickier.  Let's try opening for r+...
3438
	else
3439
	{
3440
		$upcontext['systemos'] = 'windows';
3441
3442
		foreach ($files as $k => $file)
3443
		{
3444
			// Folders can't be opened for write... but the index.php in them can ;).
3445
			if (is_dir($file))
3446
				$file .= '/index.php';
3447
3448
			// Funny enough, chmod actually does do something on windows - it removes the read only attribute.
3449
			@chmod($file, 0777);
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...
3450
			$fp = @fopen($file, 'r+');
3451
3452
			// Hmm, okay, try just for write in that case...
3453
			if (!$fp)
3454
				$fp = @fopen($file, 'w');
3455
3456
			if (!$fp)
3457
				$failure = true;
3458
			else
3459
				unset($files[$k]);
3460
			@fclose($fp);
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...
3461
		}
3462
	}
3463
3464
	if (empty($files))
3465
		return true;
3466
3467
	if (!isset($_SERVER))
3468
		return !$failure;
3469
3470
	// What still needs to be done?
3471
	$upcontext['chmod']['files'] = $files;
3472
3473
	// If it's windows it's a mess...
3474
	if ($failure && substr(__FILE__, 1, 2) == ':\\')
3475
	{
3476
		$upcontext['chmod']['ftp_error'] = 'total_mess';
3477
3478
		return false;
3479
	}
3480
	// We're going to have to use... FTP!
3481
	elseif ($failure)
3482
	{
3483
		// Load any session data we might have...
3484
		if (!isset($_POST['ftp_username']) && isset($_SESSION['installer_temp_ftp']))
3485
		{
3486
			$upcontext['chmod']['server'] = $_SESSION['installer_temp_ftp']['server'];
3487
			$upcontext['chmod']['port'] = $_SESSION['installer_temp_ftp']['port'];
3488
			$upcontext['chmod']['username'] = $_SESSION['installer_temp_ftp']['username'];
3489
			$upcontext['chmod']['password'] = $_SESSION['installer_temp_ftp']['password'];
3490
			$upcontext['chmod']['path'] = $_SESSION['installer_temp_ftp']['path'];
3491
		}
3492
		// Or have we submitted?
3493 View Code Duplication
		elseif (isset($_POST['ftp_username']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
3494
		{
3495
			$upcontext['chmod']['server'] = $_POST['ftp_server'];
3496
			$upcontext['chmod']['port'] = $_POST['ftp_port'];
3497
			$upcontext['chmod']['username'] = $_POST['ftp_username'];
3498
			$upcontext['chmod']['password'] = $_POST['ftp_password'];
3499
			$upcontext['chmod']['path'] = $_POST['ftp_path'];
3500
		}
3501
3502
		if (isset($upcontext['chmod']['username']))
3503
		{
3504
			$ftp = new ftp_connection($upcontext['chmod']['server'], $upcontext['chmod']['port'], $upcontext['chmod']['username'], $upcontext['chmod']['password']);
3505
3506
			if ($ftp->error === false)
0 ignored issues
show
Bug introduced by
The property error cannot be accessed from this context as it is declared private in class ftp_connection.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
3507
			{
3508
				// Try it without /home/abc just in case they messed up.
3509
				if (!$ftp->chdir($upcontext['chmod']['path']))
3510
				{
3511
					$upcontext['chmod']['ftp_error'] = $ftp->last_message;
0 ignored issues
show
Bug introduced by
The property last_message cannot be accessed from this context as it is declared private in class ftp_connection.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
3512
					$ftp->chdir(preg_replace('~^/home[2]?/[^/]+?~', '', $upcontext['chmod']['path']));
3513
				}
3514
			}
3515
		}
3516
3517
		if (!isset($ftp) || $ftp->error !== false)
0 ignored issues
show
Bug introduced by
The property error cannot be accessed from this context as it is declared private in class ftp_connection.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
3518
		{
3519
			if (!isset($ftp))
3520
				$ftp = new ftp_connection(null);
3521
			// Save the error so we can mess with listing...
3522
			elseif ($ftp->error !== false && !isset($upcontext['chmod']['ftp_error']))
0 ignored issues
show
Bug introduced by
The property error cannot be accessed from this context as it is declared private in class ftp_connection.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
3523
				$upcontext['chmod']['ftp_error'] = $ftp->last_message === null ? '' : $ftp->last_message;
0 ignored issues
show
Bug introduced by
The property last_message cannot be accessed from this context as it is declared private in class ftp_connection.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
3524
3525
			list ($username, $detect_path, $found_path) = $ftp->detect_path(dirname(__FILE__));
3526
3527
			if ($found_path || !isset($upcontext['chmod']['path']))
3528
				$upcontext['chmod']['path'] = $detect_path;
3529
3530
			if (!isset($upcontext['chmod']['username']))
3531
				$upcontext['chmod']['username'] = $username;
3532
3533
			return false;
3534
		}
3535
		else
3536
		{
3537
			// We want to do a relative path for FTP.
3538
			if (!in_array($upcontext['chmod']['path'], array('', '/')))
3539
			{
3540
				$ftp_root = strtr($boarddir, array($upcontext['chmod']['path'] => ''));
3541
				if (substr($ftp_root, -1) == '/' && ($upcontext['chmod']['path'] == '' || $upcontext['chmod']['path'][0] === '/'))
3542
				$ftp_root = substr($ftp_root, 0, -1);
3543
			}
3544
			else
3545
				$ftp_root = $boarddir;
3546
3547
			// Save the info for next time!
3548
			$_SESSION['installer_temp_ftp'] = array(
3549
				'server' => $upcontext['chmod']['server'],
3550
				'port' => $upcontext['chmod']['port'],
3551
				'username' => $upcontext['chmod']['username'],
3552
				'password' => $upcontext['chmod']['password'],
3553
				'path' => $upcontext['chmod']['path'],
3554
				'root' => $ftp_root,
3555
			);
3556
3557
			foreach ($files as $k => $file)
3558
			{
3559
				if (!is_writable($file))
3560
					$ftp->chmod($file, 0755);
3561
				if (!is_writable($file))
3562
					$ftp->chmod($file, 0777);
3563
3564
				// Assuming that didn't work calculate the path without the boarddir.
3565
				if (!is_writable($file))
3566
				{
3567
					if (strpos($file, $boarddir) === 0)
3568
					{
3569
						$ftp_file = strtr($file, array($_SESSION['installer_temp_ftp']['root'] => ''));
3570
						$ftp->chmod($ftp_file, 0755);
3571
						if (!is_writable($file))
3572
							$ftp->chmod($ftp_file, 0777);
3573
						// Sometimes an extra slash can help...
3574
						$ftp_file = '/' . $ftp_file;
3575
						if (!is_writable($file))
3576
							$ftp->chmod($ftp_file, 0755);
3577
						if (!is_writable($file))
3578
							$ftp->chmod($ftp_file, 0777);
3579
					}
3580
				}
3581
3582
				if (is_writable($file))
3583
					unset($files[$k]);
3584
			}
3585
3586
			$ftp->close();
3587
		}
3588
	}
3589
3590
	// What remains?
3591
	$upcontext['chmod']['files'] = $files;
3592
3593
	if (empty($files))
3594
		return true;
3595
3596
	return false;
3597
}
3598
3599
function quickFileWritable($file)
3600
{
3601
	if (is_writable($file))
3602
		return true;
3603
3604
	@chmod($file, 0755);
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...
3605
3606
	// Try 755 and 775 first since 777 doesn't always work and could be a risk...
3607
	$chmod_values = array(0755, 0775, 0777);
3608
3609
	foreach($chmod_values as $val)
3610
	{
3611
		// If it's writable, break out of the loop
3612
		if (is_writable($file))
3613
			break;
3614
		else
3615
			@chmod($file, $val);
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...
3616
	}
3617
}
3618
function smf_strtolower($string)
3619
{
3620
	global $sourcedir;
3621
	if (function_exists('mb_strtolower'))
3622
		return mb_strtolower($string, 'UTF-8');
3623
	require_once($sourcedir . '/Subs-Charset.php');
3624
	return utf8_strtolower($string);
3625
}
3626
3627
/**
3628
 * Handles converting your database to UTF-8
3629
 */
3630
function convertUtf8()
3631
{
3632
	global $upcontext, $db_character_set, $sourcedir, $smcFunc, $modSettings, $language, $db_prefix, $db_type, $command_line, $support_js, $is_debug;
3633
3634
	// First make sure they aren't already on UTF-8 before we go anywhere...
3635
	if ($db_type == 'postgresql' || ($db_character_set === 'utf8' && !empty($modSettings['global_character_set']) && $modSettings['global_character_set'] === 'UTF-8'))
3636
	{
3637
		return true;
3638
	}
3639
	else
3640
	{
3641
		$upcontext['page_title'] = 'Converting to UTF8';
3642
		$upcontext['sub_template'] = isset($_GET['xml']) ? 'convert_xml' : 'convert_utf8';
3643
3644
		// The character sets used in SMF's language files with their db equivalent.
3645
		$charsets = array(
3646
			// Armenian
3647
			'armscii8' => 'armscii8',
3648
			// Chinese-traditional.
3649
			'big5' => 'big5',
3650
			// Chinese-simplified.
3651
			'gbk' => 'gbk',
3652
			// West European.
3653
			'ISO-8859-1' => 'latin1',
3654
			// Romanian.
3655
			'ISO-8859-2' => 'latin2',
3656
			// Turkish.
3657
			'ISO-8859-9' => 'latin5',
3658
			// Latvian
3659
			'ISO-8859-13' => 'latin7',
3660
			// West European with Euro sign.
3661
			'ISO-8859-15' => 'latin9',
3662
			// Thai.
3663
			'tis-620' => 'tis620',
3664
			// Persian, Chinese, etc.
3665
			'UTF-8' => 'utf8',
3666
			// Russian.
3667
			'windows-1251' => 'cp1251',
3668
			// Greek.
3669
			'windows-1253' => 'utf8',
3670
			// Hebrew.
3671
			'windows-1255' => 'utf8',
3672
			// Arabic.
3673
			'windows-1256' => 'cp1256',
3674
		);
3675
3676
		// Get a list of character sets supported by your MySQL server.
3677
		$request = $smcFunc['db_query']('', '
3678
			SHOW CHARACTER SET',
3679
			array(
3680
			)
3681
		);
3682
		$db_charsets = array();
3683
		while ($row = $smcFunc['db_fetch_assoc']($request))
3684
			$db_charsets[] = $row['Charset'];
3685
3686
		$smcFunc['db_free_result']($request);
3687
3688
		// Character sets supported by both MySQL and SMF's language files.
3689
		$charsets = array_intersect($charsets, $db_charsets);
3690
3691
		// Use the messages.body column as indicator for the database charset.
3692
		$request = $smcFunc['db_query']('', '
3693
			SHOW FULL COLUMNS
3694
			FROM {db_prefix}messages
3695
			LIKE {string:body_like}',
3696
			array(
3697
				'body_like' => 'body',
3698
			)
3699
		);
3700
		$column_info = $smcFunc['db_fetch_assoc']($request);
3701
		$smcFunc['db_free_result']($request);
3702
3703
		// A collation looks like latin1_swedish. We only need the character set.
3704
		list($upcontext['database_charset']) = explode('_', $column_info['Collation']);
3705
		$upcontext['database_charset'] = in_array($upcontext['database_charset'], $charsets) ? array_search($upcontext['database_charset'], $charsets) : $upcontext['database_charset'];
3706
3707
		// Detect whether a fulltext index is set.
3708
		$request = $smcFunc['db_query']('', '
3709
 			SHOW INDEX
3710
	  	    FROM {db_prefix}messages',
3711
			array(
3712
			)
3713
		);
3714
3715
		$upcontext['dropping_index'] = false;
3716
3717
		// If there's a fulltext index, we need to drop it first...
3718 View Code Duplication
		if ($request !== false || $smcFunc['db_num_rows']($request) != 0)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
3719
		{
3720
			while ($row = $smcFunc['db_fetch_assoc']($request))
3721
				if ($row['Column_name'] == 'body' && (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT' || isset($row['Comment']) && $row['Comment'] == 'FULLTEXT'))
3722
					$upcontext['fulltext_index'][] = $row['Key_name'];
3723
			$smcFunc['db_free_result']($request);
3724
3725
			if (isset($upcontext['fulltext_index']))
3726
				$upcontext['fulltext_index'] = array_unique($upcontext['fulltext_index']);
3727
		}
3728
3729
		// Drop it and make a note...
3730
		if (!empty($upcontext['fulltext_index']))
3731
		{
3732
			$upcontext['dropping_index'] = true;
3733
3734
			$smcFunc['db_query']('', '
3735
  			ALTER TABLE {db_prefix}messages
3736
	  		DROP INDEX ' . implode(',
3737
		  	DROP INDEX ', $upcontext['fulltext_index']),
3738
				array(
3739
					'db_error_skip' => true,
3740
				)
3741
			);
3742
3743
			// Update the settings table
3744
			$smcFunc['db_insert']('replace',
3745
				'{db_prefix}settings',
3746
				array('variable' => 'string', 'value' => 'string'),
3747
				array('db_search_index', ''),
3748
				array('variable')
3749
			);
3750
		}
3751
3752
		// Figure out what charset we should be converting from...
3753
		$lang_charsets = array(
3754
			'arabic' => 'windows-1256',
3755
			'armenian_east' => 'armscii-8',
3756
			'armenian_west' => 'armscii-8',
3757
			'azerbaijani_latin' => 'ISO-8859-9',
3758
			'bangla' => 'UTF-8',
3759
			'belarusian' => 'ISO-8859-5',
3760
			'bulgarian' => 'windows-1251',
3761
			'cambodian' => 'UTF-8',
3762
			'chinese_simplified' => 'gbk',
3763
			'chinese_traditional' => 'big5',
3764
			'croation' => 'ISO-8859-2',
3765
			'czech' => 'ISO-8859-2',
3766
			'czech_informal' => 'ISO-8859-2',
3767
			'english_pirate' => 'UTF-8',
3768
			'esperanto' => 'ISO-8859-3',
3769
			'estonian' => 'ISO-8859-15',
3770
			'filipino_tagalog' => 'UTF-8',
3771
			'filipino_vasayan' => 'UTF-8',
3772
			'georgian' => 'UTF-8',
3773
			'greek' => 'ISO-8859-3',
3774
			'hebrew' => 'windows-1255',
3775
			'hungarian' => 'ISO-8859-2',
3776
			'irish' => 'UTF-8',
3777
			'japanese' => 'UTF-8',
3778
			'khmer' => 'UTF-8',
3779
			'korean' => 'UTF-8',
3780
			'kurdish_kurmanji' => 'ISO-8859-9',
3781
			'kurdish_sorani' => 'windows-1256',
3782
			'lao' => 'tis-620',
3783
			'latvian' => 'ISO-8859-13',
3784
			'lithuanian' => 'ISO-8859-4',
3785
			'macedonian' => 'UTF-8',
3786
			'malayalam' => 'UTF-8',
3787
			'mongolian' => 'UTF-8',
3788
			'nepali' => 'UTF-8',
3789
			'persian' => 'UTF-8',
3790
			'polish' => 'ISO-8859-2',
3791
			'romanian' => 'ISO-8859-2',
3792
			'russian' => 'windows-1252',
3793
			'sakha' => 'UTF-8',
3794
			'serbian_cyrillic' => 'ISO-8859-5',
3795
			'serbian_latin' => 'ISO-8859-2',
3796
			'sinhala' => 'UTF-8',
3797
			'slovak' => 'ISO-8859-2',
3798
			'slovenian' => 'ISO-8859-2',
3799
			'telugu' => 'UTF-8',
3800
			'thai' => 'tis-620',
3801
			'turkish' => 'ISO-8859-9',
3802
			'turkmen' => 'ISO-8859-9',
3803
			'ukranian' => 'windows-1251',
3804
			'urdu' => 'UTF-8',
3805
			'uzbek_cyrillic' => 'ISO-8859-5',
3806
			'uzbek_latin' => 'ISO-8859-5',
3807
			'vietnamese' => 'UTF-8',
3808
			'yoruba' => 'UTF-8'
3809
		);
3810
3811
		// Default to ISO-8859-1 unless we detected another supported charset
3812
		$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';
3813
3814
		$upcontext['charset_list'] = array_keys($charsets);
3815
3816
		// Translation table for the character sets not native for MySQL.
3817
		$translation_tables = array(
3818
			'windows-1255' => array(
3819
				'0x81' => '\'\'',		'0x8A' => '\'\'',		'0x8C' => '\'\'',
3820
				'0x8D' => '\'\'',		'0x8E' => '\'\'',		'0x8F' => '\'\'',
3821
				'0x90' => '\'\'',		'0x9A' => '\'\'',		'0x9C' => '\'\'',
3822
				'0x9D' => '\'\'',		'0x9E' => '\'\'',		'0x9F' => '\'\'',
3823
				'0xCA' => '\'\'',		'0xD9' => '\'\'',		'0xDA' => '\'\'',
3824
				'0xDB' => '\'\'',		'0xDC' => '\'\'',		'0xDD' => '\'\'',
3825
				'0xDE' => '\'\'',		'0xDF' => '\'\'',		'0xFB' => '\'\'',
3826
				'0xFC' => '\'\'',		'0xFF' => '\'\'',		'0xC2' => '0xFF',
3827
				'0x80' => '0xFC',		'0xE2' => '0xFB',		'0xA0' => '0xC2A0',
3828
				'0xA1' => '0xC2A1',		'0xA2' => '0xC2A2',		'0xA3' => '0xC2A3',
3829
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
3830
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
3831
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
3832
				'0xAF' => '0xC2AF',		'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',
3833
				'0xB2' => '0xC2B2',		'0xB3' => '0xC2B3',		'0xB4' => '0xC2B4',
3834
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
3835
				'0xB8' => '0xC2B8',		'0xB9' => '0xC2B9',		'0xBB' => '0xC2BB',
3836
				'0xBC' => '0xC2BC',		'0xBD' => '0xC2BD',		'0xBE' => '0xC2BE',
3837
				'0xBF' => '0xC2BF',		'0xD7' => '0xD7B3',		'0xD1' => '0xD781',
3838
				'0xD4' => '0xD7B0',		'0xD5' => '0xD7B1',		'0xD6' => '0xD7B2',
3839
				'0xE0' => '0xD790',		'0xEA' => '0xD79A',		'0xEC' => '0xD79C',
3840
				'0xED' => '0xD79D',		'0xEE' => '0xD79E',		'0xEF' => '0xD79F',
3841
				'0xF0' => '0xD7A0',		'0xF1' => '0xD7A1',		'0xF2' => '0xD7A2',
3842
				'0xF3' => '0xD7A3',		'0xF5' => '0xD7A5',		'0xF6' => '0xD7A6',
3843
				'0xF7' => '0xD7A7',		'0xF8' => '0xD7A8',		'0xF9' => '0xD7A9',
3844
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
3845
				'0x86' => '0xE280A0',	'0x87' => '0xE280A1',	'0x89' => '0xE280B0',
3846
				'0x8B' => '0xE280B9',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
3847
				'0x95' => '0xE280A2',	'0x97' => '0xE28094',	'0x99' => '0xE284A2',
3848
				'0xC0' => '0xD6B0',		'0xC1' => '0xD6B1',		'0xC3' => '0xD6B3',
3849
				'0xC4' => '0xD6B4',		'0xC5' => '0xD6B5',		'0xC6' => '0xD6B6',
3850
				'0xC7' => '0xD6B7',		'0xC8' => '0xD6B8',		'0xC9' => '0xD6B9',
3851
				'0xCB' => '0xD6BB',		'0xCC' => '0xD6BC',		'0xCD' => '0xD6BD',
3852
				'0xCE' => '0xD6BE',		'0xCF' => '0xD6BF',		'0xD0' => '0xD780',
3853
				'0xD2' => '0xD782',		'0xE3' => '0xD793',		'0xE4' => '0xD794',
3854
				'0xE5' => '0xD795',		'0xE7' => '0xD797',		'0xE9' => '0xD799',
3855
				'0xFD' => '0xE2808E',	'0xFE' => '0xE2808F',	'0x92' => '0xE28099',
3856
				'0x83' => '0xC692',		'0xD3' => '0xD783',		'0x88' => '0xCB86',
3857
				'0x98' => '0xCB9C',		'0x91' => '0xE28098',	'0x96' => '0xE28093',
3858
				'0xBA' => '0xC3B7',		'0x9B' => '0xE280BA',	'0xAA' => '0xC397',
3859
				'0xA4' => '0xE282AA',	'0xE1' => '0xD791',		'0xE6' => '0xD796',
3860
				'0xE8' => '0xD798',		'0xEB' => '0xD79B',		'0xF4' => '0xD7A4',
3861
				'0xFA' => '0xD7AA',		'0xFF' => '0xD6B2',		'0xFC' => '0xE282AC',
3862
				'0xFB' => '0xD792',
3863
			),
3864
			'windows-1253' => array(
3865
				'0x81' => '\'\'',			'0x88' => '\'\'',			'0x8A' => '\'\'',
3866
				'0x8C' => '\'\'',			'0x8D' => '\'\'',			'0x8E' => '\'\'',
3867
				'0x8F' => '\'\'',			'0x90' => '\'\'',			'0x98' => '\'\'',
3868
				'0x9A' => '\'\'',			'0x9C' => '\'\'',			'0x9D' => '\'\'',
3869
				'0x9E' => '\'\'',			'0x9F' => '\'\'',			'0xAA' => '\'\'',
3870
				'0xD2' => '\'\'',			'0xFF' => '\'\'',			'0xCE' => '0xCE9E',
3871
				'0xB8' => '0xCE88',		'0xBA' => '0xCE8A',		'0xBC' => '0xCE8C',
3872
				'0xBE' => '0xCE8E',		'0xBF' => '0xCE8F',		'0xC0' => '0xCE90',
3873
				'0xC8' => '0xCE98',		'0xCA' => '0xCE9A',		'0xCC' => '0xCE9C',
3874
				'0xCD' => '0xCE9D',		'0xCF' => '0xCE9F',		'0xDA' => '0xCEAA',
3875
				'0xE8' => '0xCEB8',		'0xEA' => '0xCEBA',		'0xEC' => '0xCEBC',
3876
				'0xEE' => '0xCEBE',		'0xEF' => '0xCEBF',		'0xC2' => '0xFF',
3877
				'0xBD' => '0xC2BD',		'0xED' => '0xCEBD',		'0xB2' => '0xC2B2',
3878
				'0xA0' => '0xC2A0',		'0xA3' => '0xC2A3',		'0xA4' => '0xC2A4',
3879
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
3880
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
3881
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
3882
				'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',		'0xB3' => '0xC2B3',
3883
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
3884
				'0xBB' => '0xC2BB',		'0xE2' => '0xCEB2',		'0x80' => '0xD2',
3885
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
3886
				'0x86' => '0xE280A0',	'0xA1' => '0xCE85',		'0xA2' => '0xCE86',
3887
				'0x87' => '0xE280A1',	'0x89' => '0xE280B0',	'0xB9' => '0xCE89',
3888
				'0x8B' => '0xE280B9',	'0x91' => '0xE28098',	'0x99' => '0xE284A2',
3889
				'0x92' => '0xE28099',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
3890
				'0x95' => '0xE280A2',	'0x96' => '0xE28093',	'0x97' => '0xE28094',
3891
				'0x9B' => '0xE280BA',	'0xAF' => '0xE28095',	'0xB4' => '0xCE84',
3892
				'0xC1' => '0xCE91',		'0xC3' => '0xCE93',		'0xC4' => '0xCE94',
3893
				'0xC5' => '0xCE95',		'0xC6' => '0xCE96',		'0x83' => '0xC692',
3894
				'0xC7' => '0xCE97',		'0xC9' => '0xCE99',		'0xCB' => '0xCE9B',
3895
				'0xD0' => '0xCEA0',		'0xD1' => '0xCEA1',		'0xD3' => '0xCEA3',
3896
				'0xD4' => '0xCEA4',		'0xD5' => '0xCEA5',		'0xD6' => '0xCEA6',
3897
				'0xD7' => '0xCEA7',		'0xD8' => '0xCEA8',		'0xD9' => '0xCEA9',
3898
				'0xDB' => '0xCEAB',		'0xDC' => '0xCEAC',		'0xDD' => '0xCEAD',
3899
				'0xDE' => '0xCEAE',		'0xDF' => '0xCEAF',		'0xE0' => '0xCEB0',
3900
				'0xE1' => '0xCEB1',		'0xE3' => '0xCEB3',		'0xE4' => '0xCEB4',
3901
				'0xE5' => '0xCEB5',		'0xE6' => '0xCEB6',		'0xE7' => '0xCEB7',
3902
				'0xE9' => '0xCEB9',		'0xEB' => '0xCEBB',		'0xF0' => '0xCF80',
3903
				'0xF1' => '0xCF81',		'0xF2' => '0xCF82',		'0xF3' => '0xCF83',
3904
				'0xF4' => '0xCF84',		'0xF5' => '0xCF85',		'0xF6' => '0xCF86',
3905
				'0xF7' => '0xCF87',		'0xF8' => '0xCF88',		'0xF9' => '0xCF89',
3906
				'0xFA' => '0xCF8A',		'0xFB' => '0xCF8B',		'0xFC' => '0xCF8C',
3907
				'0xFD' => '0xCF8D',		'0xFE' => '0xCF8E',		'0xFF' => '0xCE92',
3908
				'0xD2' => '0xE282AC',
3909
			),
3910
		);
3911
3912
		// Make some preparations.
3913
		if (isset($translation_tables[$upcontext['charset_detected']]))
3914
		{
3915
			$replace = '%field%';
3916
3917
			// Build a huge REPLACE statement...
3918
			foreach ($translation_tables[$upcontext['charset_detected']] as $from => $to)
3919
				$replace = 'REPLACE(' . $replace . ', ' . $from . ', ' . $to . ')';
3920
		}
3921
3922
		// Get a list of table names ahead of time... This makes it easier to set our substep and such
3923
		db_extend();
3924
		$queryTables = $smcFunc['db_list_tables'](false, $db_prefix);
3925
3926
		$upcontext['table_count'] = count($queryTables);
3927
		$file_steps = $upcontext['table_count'];
0 ignored issues
show
Unused Code introduced by
$file_steps 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...
3928
3929
		for($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
3930
		{
3931
			$table = $queryTables[$_GET['substep']];
3932
3933
			// Do we need to pause?
3934
			nextSubstep($substep);
3935
3936
			$getTableStatus = $smcFunc['db_query']('', '
3937
				SHOW TABLE STATUS
3938
				LIKE {string:table_name}',
3939
				array(
3940
					'table_name' => str_replace('_', '\_', $table)
3941
				)
3942
			);
3943
3944
			// Only one row so we can just fetch_assoc and free the result...
3945
			$table_info = $smcFunc['db_fetch_assoc']($getTableStatus);
3946
			$smcFunc['db_free_result']($getTableStatus);
3947
3948
			$upcontext['cur_table_num'] = $_GET['substep'];
3949
			$upcontext['cur_table_name'] = $table_info['Name'];
3950
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3951
3952
			// Just to make sure it doesn't time out.
3953
			if (function_exists('apache_reset_timeout'))
3954
				@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...
3955
3956
			$table_charsets = array();
3957
3958
			// Loop through each column.
3959
			$queryColumns = $smcFunc['db_query']('', '
3960
				SHOW FULL COLUMNS
3961
				FROM ' . $table_info['Name'],
3962
				array(
3963
				)
3964
			);
3965
			while ($column_info = $smcFunc['db_fetch_assoc']($queryColumns))
3966
			{
3967
				// Only text'ish columns have a character set and need converting.
3968
				if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false)
3969
				{
3970
					$collation = empty($column_info['Collation']) || $column_info['Collation'] === 'NULL' ? $table_info['Collation'] : $column_info['Collation'];
3971
					if (!empty($collation) && $collation !== 'NULL')
3972
					{
3973
						list($charset) = explode('_', $collation);
3974
3975
						if (!isset($table_charsets[$charset]))
3976
							$table_charsets[$charset] = array();
3977
3978
						$table_charsets[$charset][] = $column_info;
3979
					}
3980
				}
3981
			}
3982
			$smcFunc['db_free_result']($queryColumns);
3983
3984
			// Only change the column if the data doesn't match the current charset.
3985
			if ((count($table_charsets) === 1 && key($table_charsets) !== $charsets[$upcontext['charset_detected']]) || count($table_charsets) > 1)
3986
			{
3987
				$updates_blob = '';
3988
				$updates_text = '';
3989
				foreach ($table_charsets as $charset => $columns)
3990
				{
3991
					if ($charset !== $charsets[$upcontext['charset_detected']])
3992
					{
3993
						foreach ($columns as $column)
3994
						{
3995
							$updates_blob .= '
3996
								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'] . '\'') . ',';
3997
							$updates_text .= '
3998
								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'] . '\'') . ',';
3999
						}
4000
					}
4001
				}
4002
4003
				// Change the columns to binary form.
4004
				$smcFunc['db_query']('', '
4005
					ALTER TABLE {raw:table_name}{raw:updates_blob}',
4006
					array(
4007
						'table_name' => $table_info['Name'],
4008
						'updates_blob' => substr($updates_blob, 0, -1),
4009
					)
4010
				);
4011
4012
				// Convert the character set if MySQL has no native support for it.
4013
				if (isset($translation_tables[$upcontext['charset_detected']]))
4014
				{
4015
					$update = '';
4016
					foreach ($table_charsets as $charset => $columns)
4017
						foreach ($columns as $column)
4018
							$update .= '
4019
								' . $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...
4020
4021
					$smcFunc['db_query']('', '
4022
						UPDATE {raw:table_name}
4023
						SET {raw:updates}',
4024
						array(
4025
							'table_name' => $table_info['Name'],
4026
							'updates' => substr($update, 0, -1),
4027
						)
4028
					);
4029
				}
4030
4031
				// Change the columns back, but with the proper character set.
4032
				$smcFunc['db_query']('', '
4033
					ALTER TABLE {raw:table_name}{raw:updates_text}',
4034
					array(
4035
						'table_name' => $table_info['Name'],
4036
						'updates_text' => substr($updates_text, 0, -1),
4037
					)
4038
				);
4039
			}
4040
4041
			// Now do the actual conversion (if still needed).
4042
			if ($charsets[$upcontext['charset_detected']] !== 'utf8')
4043
			{
4044
				if ($command_line)
4045
					echo 'Converting table ' . $table_info['Name'] . ' to UTF-8...';
4046
4047
				$smcFunc['db_query']('', '
4048
					ALTER TABLE {raw:table_name}
4049
					CONVERT TO CHARACTER SET utf8',
4050
						array(
4051
								'table_name' => $table_info['Name'],
4052
						)
4053
				);
4054
4055
				if ($command_line)
4056
					echo " done.\n";
4057
			}
4058
		}
4059
4060
		$prev_charset = empty($translation_tables[$upcontext['charset_detected']]) ? $charsets[$upcontext['charset_detected']] : $translation_tables[$upcontext['charset_detected']];
4061
4062
		$smcFunc['db_insert']('replace',
4063
			'{db_prefix}settings',
4064
			array('variable' => 'string', 'value' => 'string'),
4065
			array(array('global_character_set', 'UTF-8'), array('previousCharacterSet', $prev_charset)),
4066
			array('variable')
4067
		);
4068
4069
		// Store it in Settings.php too because it's needed before db connection.
4070
		// Hopefully this works...
4071
		require_once($sourcedir . '/Subs-Admin.php');
4072
		updateSettingsFile(array('db_character_set' => '\'utf8\''));
4073
4074
		// The conversion might have messed up some serialized strings. Fix them!
4075
		$request = $smcFunc['db_query']('', '
4076
			SELECT id_action, extra
4077
			FROM {db_prefix}log_actions
4078
			WHERE action IN ({string:remove}, {string:delete})',
4079
			array(
4080
				'remove' => 'remove',
4081
				'delete' => 'delete',
4082
			)
4083
		);
4084 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
4085
		{
4086
			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)
4087
				$smcFunc['db_query']('', '
4088
					UPDATE {db_prefix}log_actions
4089
					SET extra = {string:extra}
4090
					WHERE id_action = {int:current_action}',
4091
					array(
4092
						'current_action' => $row['id_action'],
4093
						'extra' => $matches[1] . strlen($matches[3]) . ':"' . $matches[3] . '"' . $matches[4],
4094
					)
4095
				);
4096
		}
4097
		$smcFunc['db_free_result']($request);
4098
4099
		if ($upcontext['dropping_index'] && $command_line)
4100
		{
4101
			echo "\nYour fulltext search index was dropped to facilitate the conversion. You will need to recreate it.";
4102
			flush();
4103
		}
4104
	}
4105
4106
	return true;
4107
}
4108
4109
function serialize_to_json()
4110
{
4111
	global $command_line, $smcFunc, $modSettings, $sourcedir, $upcontext, $support_js, $is_debug;
4112
4113
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'serialize_json_xml' : 'serialize_json';
4114
	// First thing's first - did we already do this?
4115
	if (!empty($modSettings['json_done']))
4116
	{
4117
		if ($command_line)
4118
			return DeleteUpgrade();
4119
		else
4120
			return true;
4121
	}
4122
4123
	// Done it already - js wise?
4124
	if (!empty($_POST['json_done']))
4125
		return true;
4126
4127
	// List of tables affected by this function
4128
	// name => array('key', col1[,col2|true[,col3]])
4129
	// If 3rd item in array is true, it indicates that col1 could be empty...
4130
	$tables = array(
4131
		'background_tasks' => array('id_task', 'task_data'),
4132
		'log_actions' => array('id_action', 'extra'),
4133
		'log_online' => array('session', 'url'),
4134
		'log_packages' => array('id_install', 'db_changes', 'failed_steps', 'credits'),
4135
		'log_spider_hits' => array('id_hit', 'url'),
4136
		'log_subscribed' => array('id_sublog', 'pending_details'),
4137
		'pm_rules' => array('id_rule', 'criteria', 'actions'),
4138
		'qanda' => array('id_question', 'answers'),
4139
		'subscriptions' => array('id_subscribe', 'cost'),
4140
		'user_alerts' => array('id_alert', 'extra', true),
4141
		'user_drafts' => array('id_draft', 'to_list', true),
4142
		// These last two are a bit different - we'll handle those separately
4143
		'settings' => array(),
4144
		'themes' => array()
4145
	);
4146
4147
	// Set up some context stuff...
4148
	// Because we're not using numeric indices, we need this to figure out the current table name...
4149
	$keys = array_keys($tables);
4150
4151
	$upcontext['table_count'] = 13;
4152
	$upcontext['cur_table_num'] = $_GET['substep'];
4153
	$upcontext['cur_table_name'] = isset($keys[$_GET['substep']]) ? $keys[$_GET['substep']] : $keys[0];
4154
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
4155
	$file_steps = $upcontext['table_count'];
0 ignored issues
show
Unused Code introduced by
$file_steps 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...
4156
4157 View Code Duplication
	foreach($keys as $id => $table)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
4158
		if ($id < $_GET['substep'])
4159
			$upcontext['previous_tables'][] = $table;
4160
4161
	if ($command_line)
4162
		echo 'Converting data from serialize() to json_encode().';
4163
4164
	if (!$support_js || isset($_GET['xml']))
4165
	{
4166
		// Fix the data in each table
4167
		for ($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
4168
		{
4169
			$upcontext['cur_table_name'] = isset($keys[$substep + 1]) ? $keys[$substep + 1] : $keys[$substep];
4170
			$upcontext['cur_table_num'] = $substep + 1;
4171
4172
			$upcontext['step_progress'] = (int)(($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
4173
4174
			// Do we need to pause?
4175
			nextSubstep($substep);
4176
4177
			// Initialize a few things...
4178
			$where = '';
4179
			$vars = array();
4180
			$table = $keys[$substep];
4181
			$info = $tables[$table];
4182
4183
			// Now the fun - build our queries and all that fun stuff
4184
			if ($table == 'settings')
4185
			{
4186
				// Now a few settings...
4187
				$serialized_settings = array(
4188
					'attachment_basedirectories',
4189
					'attachmentUploadDir',
4190
					'cal_today_birthday',
4191
					'cal_today_event',
4192
					'cal_today_holiday',
4193
					'displayFields',
4194
					'last_attachments_directory',
4195
					'memberlist_cache',
4196
					'search_index_custom_config',
4197
					'spider_name_cache'
4198
				);
4199
4200
				// Loop through and fix these...
4201
				$new_settings = array();
4202
				if ($command_line)
4203
					echo "\n" . 'Fixing some settings...';
4204
4205
				foreach ($serialized_settings as $var)
4206
				{
4207
					if (isset($modSettings[$var]))
4208
					{
4209
						// Attempt to unserialize the setting
4210
						$temp = @safe_unserialize($modSettings[$var]);
4211
						if (!$temp && $command_line)
4212
							echo "\n - Failed to unserialize the '" . $var . "' setting. Skipping.";
4213
						elseif ($temp !== false)
4214
							$new_settings[$var] = json_encode($temp);
4215
					}
4216
				}
4217
4218
				// Update everything at once
4219
				if (!function_exists('cache_put_data'))
4220
					require_once($sourcedir . '/Load.php');
4221
				updateSettings($new_settings, true);
4222
4223
				if ($command_line)
4224
					echo ' done.';
4225
			}
4226
			elseif ($table == 'themes')
4227
			{
4228
				// Finally, fix the admin prefs. Unfortunately this is stored per theme, but hopefully they only have one theme installed at this point...
4229
				$query = $smcFunc['db_query']('', '
4230
					SELECT * FROM {db_prefix}themes
4231
					WHERE variable = {string:admin_prefs}',
4232
						array(
4233
							'admin_prefs' => 'admin_preferences'
4234
						)
4235
				);
4236
4237
				if ($smcFunc['db_num_rows']($query) != 0)
4238
				{
4239
					while ($row = $smcFunc['db_fetch_assoc']($query))
4240
					{
4241
						$temp = @safe_unserialize($row['admin_preferences']);
4242
4243
						if ($command_line)
4244
						{
4245
							if ($temp === false)
4246
								echo "\n" . 'Unserialize of admin_preferences for user ' . $row['id_member'] . ' failed. Skipping.';
4247
							else
4248
								echo "\n" . 'Fixing admin preferences...';
4249
						}
4250
4251
						if ($temp !== false)
4252
						{
4253
							$row['admin_preferences'] = json_encode($temp);
4254
4255
							// Even though we have all values from the table, UPDATE is still faster than REPLACE
4256
							$smcFunc['db_query']('', '
4257
								UPDATE {db_prefix}themes
4258
								SET value = {string:prefs}
4259
								WHERE id_theme = {int:theme}
4260
									AND id_member = {int:member}',
4261
								array(
4262
									'prefs' => $row['admin_preferences'],
4263
									'theme' => $row['id_theme'],
4264
									'member' => $row['id_member']
4265
								)
4266
							);
4267
4268
							if ($is_debug || $command_line)
4269
								echo ' done.';
4270
						}
4271
					}
4272
4273
					$smcFunc['db_free_result']($query);
4274
				}
4275
			}
4276
			else
4277
			{
4278
				// First item is always the key...
4279
				$key = $info[0];
4280
				unset($info[0]);
4281
4282
				// Now we know what columns we have and such...
4283
				if (count($info) == 2 && $info[2] === true)
4284
				{
4285
					$col_select = $info[1];
4286
					$where = ' WHERE ' . $info[1] . ' != {empty}';
4287
				}
4288
				else
4289
				{
4290
					$col_select = implode(', ', $info);
4291
				}
4292
4293
				$query = $smcFunc['db_query']('', '
4294
					SELECT ' . $key . ', ' . $col_select . '
4295
					FROM {db_prefix}' . $table . $where,
4296
					array()
4297
				);
4298
4299
				if ($smcFunc['db_num_rows']($query) != 0)
4300
				{
4301
					if ($command_line)
4302
					{
4303
						echo "\n" . ' +++ Fixing the "' . $table . '" table...';
4304
						flush();
4305
					}
4306
4307
					while ($row = $smcFunc['db_fetch_assoc']($query))
4308
					{
4309
						$update = '';
4310
4311
						// We already know what our key is...
4312
						foreach ($info as $col)
4313
						{
4314
							if ($col !== true && $row[$col] != '')
4315
							{
4316
								$temp = @safe_unserialize($row[$col]);
4317
4318
								if ($temp === false && $command_line)
4319
								{
4320
									echo "\nFailed to unserialize " . $row[$col] . "... Skipping\n";
4321
								}
4322
								else
4323
								{
4324
									$row[$col] = json_encode($temp);
4325
4326
									// Build our SET string and variables array
4327
									$update .= (empty($update) ? '' : ', ') . $col . ' = {string:' . $col . '}';
4328
									$vars[$col] = $row[$col];
4329
								}
4330
							}
4331
						}
4332
4333
						$vars[$key] = $row[$key];
4334
4335
						// In a few cases, we might have empty data, so don't try to update in those situations...
4336
						if (!empty($update))
4337
						{
4338
							$smcFunc['db_query']('', '
4339
								UPDATE {db_prefix}' . $table . '
4340
								SET ' . $update . '
4341
								WHERE ' . $key . ' = {' . ($key == 'session' ? 'string' : 'int') . ':' . $key . '}',
4342
								$vars
4343
							);
4344
						}
4345
					}
4346
4347
					if ($command_line)
4348
						echo ' done.';
4349
4350
					// Free up some memory...
4351
					$smcFunc['db_free_result']($query);
4352
				}
4353
			}
4354
			// If this is XML to keep it nice for the user do one table at a time anyway!
4355
			if (isset($_GET['xml']))
4356
				return upgradeExit();
4357
		}
4358
4359
		if ($command_line)
4360
		{
4361
			echo "\n" . 'Successful.' . "\n";
4362
			flush();
4363
		}
4364
		$upcontext['step_progress'] = 100;
4365
4366
		// Last but not least, insert a dummy setting so we don't have to do this again in the future...
4367
		updateSettings(array('json_done' => true));
4368
4369
		$_GET['substep'] = 0;
4370
		// Make sure we move on!
4371
		if ($command_line)
4372
			return DeleteUpgrade();
4373
4374
		return true;
4375
	}
4376
4377
	// If this fails we just move on to deleting the upgrade anyway...
4378
	$_GET['substep'] = 0;
4379
	return false;
4380
}
4381
4382
/******************************************************************************
4383
******************* Templates are below this point ****************************
4384
******************************************************************************/
4385
4386
// This is what is displayed if there's any chmod to be done. If not it returns nothing...
4387
function template_chmod()
4388
{
4389
	global $upcontext, $txt, $settings;
4390
4391
	// Don't call me twice!
4392
	if (!empty($upcontext['chmod_called']))
4393
		return;
4394
4395
	$upcontext['chmod_called'] = true;
4396
4397
	// Nothing?
4398
	if (empty($upcontext['chmod']['files']) && empty($upcontext['chmod']['ftp_error']))
4399
		return;
4400
4401
	// Was it a problem with Windows?
4402
	if (!empty($upcontext['chmod']['ftp_error']) && $upcontext['chmod']['ftp_error'] == 'total_mess')
4403
	{
4404
		echo '
4405
			<div class="error_message">
4406
				<div style="color: red;">The following files need to be writable to continue the upgrade. Please ensure the Windows permissions are correctly set to allow this:</div>
4407
				<ul style="margin: 2.5ex; font-family: monospace;">
4408
				<li>' . implode('</li>
4409
				<li>', $upcontext['chmod']['files']). '</li>
4410
			</ul>
4411
			</div>';
4412
4413
		return false;
4414
	}
4415
4416
	echo '
4417
		<div class="panel">
4418
			<h2>Your FTP connection information</h2>
4419
			<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>
4420
			<script>
4421
				function warning_popup()
4422
				{
4423
					popup = window.open(\'\',\'popup\',\'height=150,width=400,scrollbars=yes\');
4424
					var content = popup.document;
4425
					content.write(\'<!DOCTYPE html>\n\');
4426
					content.write(\'<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>\n\t<head>\n\t\t<meta name="robots" content="noindex">\n\t\t\');
4427
					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\');
4428
					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\');
4429
					content.write(\'<p>', implode('<br>\n\t\t\t', $upcontext['chmod']['files']), '</p>\n\t\t\t\');';
4430
4431
	if (isset($upcontext['systemos']) && $upcontext['systemos'] == 'linux')
4432
		echo '
4433
					content.write(\'<hr />\n\t\t\t\');
4434
					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\');
4435
					content.write(\'<tt># chmod a+w ', implode(' ', $upcontext['chmod']['files']), '</tt>\n\t\t\t\');';
4436
4437
	echo '
4438
					content.write(\'<a href="javascript:self.close();">close</a>\n\t\t</div>\n\t</body>\n</html>\');
4439
					content.close();
4440
				}
4441
		</script>';
4442
4443
	if (!empty($upcontext['chmod']['ftp_error']))
4444
		echo '
4445
			<div class="error_message">
4446
				<div style="color: red;">
4447
					The following error was encountered when trying to connect:<br>
4448
					<br>
4449
					<code>', $upcontext['chmod']['ftp_error'], '</code>
4450
				</div>
4451
			</div>
4452
			<br>';
4453
4454
	if (empty($upcontext['chmod_in_form']))
4455
		echo '
4456
	<form action="', $upcontext['form_url'], '" method="post">';
4457
4458
	echo '
4459
		<table width="520" border="0" align="center" style="margin-bottom: 1ex;">
4460
			<tr>
4461
				<td width="26%" valign="top" class="textbox"><label for="ftp_server">', $txt['ftp_server'], ':</label></td>
4462
				<td>
4463
					<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>
4464
					<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">
4465
					<div class="smalltext block">', $txt['ftp_server_info'], '</div>
4466
				</td>
4467
			</tr><tr>
4468
				<td width="26%" valign="top" class="textbox"><label for="ftp_username">', $txt['ftp_username'], ':</label></td>
4469
				<td>
4470
					<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">
4471
					<div class="smalltext block">', $txt['ftp_username_info'], '</div>
4472
				</td>
4473
			</tr><tr>
4474
				<td width="26%" valign="top" class="textbox"><label for="ftp_password">', $txt['ftp_password'], ':</label></td>
4475
				<td>
4476
					<input type="password" size="50" name="ftp_password" id="ftp_password" style="width: 99%;" class="input_password">
4477
					<div class="smalltext block">', $txt['ftp_password_info'], '</div>
4478
				</td>
4479
			</tr><tr>
4480
				<td width="26%" valign="top" class="textbox"><label for="ftp_path">', $txt['ftp_path'], ':</label></td>
4481
				<td style="padding-bottom: 1ex;">
4482
					<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">
4483
					<div class="smalltext block">', !empty($upcontext['chmod']['path']) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'], '</div>
4484
				</td>
4485
			</tr>
4486
		</table>
4487
4488
		<div class="righttext" style="margin: 1ex;"><input type="submit" value="', $txt['ftp_connect'], '" class="button_submit"></div>
4489
	</div>';
4490
4491
	if (empty($upcontext['chmod_in_form']))
4492
		echo '
4493
	</form>';
4494
}
4495
4496
function template_upgrade_above()
4497
{
4498
	global $modSettings, $txt, $settings, $upcontext, $upgradeurl;
4499
4500
	echo '<!DOCTYPE html>
4501
<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>
4502
	<head>
4503
		<meta charset="', isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8', '">
4504
		<meta name="robots" content="noindex">
4505
		<title>', $txt['upgrade_upgrade_utility'], '</title>
4506
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/index.css?alp21">
4507
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/install.css?alp21">
4508
		', $txt['lang_rtl'] == true ? '<link rel="stylesheet" href="' . $settings['default_theme_url'] . '/css/rtl.css?alp21">' : '' , '
4509
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
4510
		<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
4511
		<script>
4512
			var smf_scripturl = \'', $upgradeurl, '\';
4513
			var smf_charset = \'', (empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'UTF-8' : $txt['lang_character_set']) : $modSettings['global_character_set']), '\';
4514
			var startPercent = ', $upcontext['overall_percent'], ';
4515
4516
			// This function dynamically updates the step progress bar - and overall one as required.
4517
			function updateStepProgress(current, max, overall_weight)
4518
			{
4519
				// What out the actual percent.
4520
				var width = parseInt((current / max) * 100);
4521
				if (document.getElementById(\'step_progress_upgrade\'))
4522
				{
4523
					document.getElementById(\'step_progress_upgrade\').style.width = width + "%";
4524
					setInnerHTML(document.getElementById(\'step_text_upgrade\'), width + "%");
4525
				}
4526
				if (overall_weight && document.getElementById(\'overall_progress_upgrade\'))
4527
				{
4528
					overall_width = parseInt(startPercent + width * (overall_weight / 100));
4529
					document.getElementById(\'overall_progress_upgrade\').style.width = overall_width + "%";
4530
					setInnerHTML(document.getElementById(\'overall_text_upgrade\'), overall_width + "%");
4531
				}
4532
			}
4533
		</script>
4534
	</head>
4535
	<body>
4536
	<div id="footerfix">
4537
		<div id="header">
4538
			<h1 class="forumtitle">', $txt['upgrade_upgrade_utility'], '</h1>
4539
			<img id="smflogo" src="', $settings['default_theme_url'], '/images/smflogo.png" alt="Simple Machines Forum" title="Simple Machines Forum">
4540
		</div>
4541
	<div id="wrapper">
4542
	<div id="upper_section">
4543
		<div id="main_content_section">
4544
			<div id="main_steps">
4545
				<h2>', $txt['upgrade_progress'], '</h2>
4546
				<ul>';
4547
4548 View Code Duplication
	foreach ($upcontext['steps'] as $num => $step)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
4549
		echo '
4550
						<li class="', $num < $upcontext['current_step'] ? 'stepdone' : ($num == $upcontext['current_step'] ? 'stepcurrent' : 'stepwaiting'), '">', $txt['upgrade_step'], ' ', $step[0], ': ', $step[1], '</li>';
4551
4552
	echo '
4553
					</ul>
4554
			</div>
4555
4556
			<div id="progress">
4557
				<div id="overall_text_upgrade">', $upcontext['overall_percent'], '%</div>
4558
				<div id="overall_progress_upgrade" style="width: ', $upcontext['overall_percent'], '%;">&nbsp;</div>
4559
				<div class="over_progress">', $txt['upgrade_overall_progress'], '</div>
4560
			</div>';
4561
4562
	if (isset($upcontext['step_progress']))
4563
		echo '
4564
				<br>
4565
				<br>
4566
				<div id="progress">
4567
					<div id="step_text_upgrade">', $upcontext['step_progress'], '%</div>
4568
					<div id="step_progress_upgrade" style="width: ', $upcontext['step_progress'], '%;background-color: #ffd000;">&nbsp;</div>
4569
					<div class="over_progress">', $txt['upgrade_step_progress'], '</div>
4570
					</div>';
4571
4572
	echo '
4573
				<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>
4574
				<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', ';">
4575
					<div id="substep_text" style="color: #000; position: absolute; margin-left: -5em;">', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : '', '%</div>
4576
				<div id="substep_progress" style="width: ', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%; height: 12pt; z-index: 1; background-color: #eebaf4;">&nbsp;</div>
4577
								</div>';
4578
4579
	// How long have we been running this?
4580
	$elapsed = time() - $upcontext['started'];
4581
	$mins = (int) ($elapsed / 60);
4582
	$seconds = $elapsed - $mins * 60;
4583
	echo '
4584
								<br> <br> <br> <br> <br>
4585
								<div class="smalltext" style="padding: 5px; text-align: center;"><br>', $txt['upgrade_time_elapsed'], ':
4586
									<span id="mins_elapsed">', $mins, '</span> ', $txt['upgrade_time_mins'], ', <span id="secs_elapsed">', $seconds, '</span> ', $txt['upgrade_time_secs'], '.
4587
								</div>';
4588
	echo '
4589
			</div>
4590
			</div>
4591
			<div id="content_section">
4592
			<div id="main_screen" class="clear">
4593
				<h2>', $upcontext['page_title'], '</h2>
4594
				<div class="panel">
4595
					<div style="max-height: 360px; overflow: auto;">';
4596
}
4597
4598
function template_upgrade_below()
4599
{
4600
	global $upcontext, $txt;
4601
4602
	if (!empty($upcontext['pause']))
4603
		echo '
4604
								<em>', $txt['upgrade_incomplete'], '.</em><br>
4605
4606
								<h2 style="margin-top: 2ex;">', $txt['upgrade_not_quite_done'], '</h2>
4607
								<h3>
4608
									', $txt['upgrade_paused_overload'], '
4609
								</h3>';
4610
4611
	if (!empty($upcontext['custom_warning']))
4612
		echo '
4613
								<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
4614
									<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4615
									<strong style="text-decoration: underline;">', $txt['upgrade_note'], '</strong><br>
4616
									<div style="padding-left: 6ex;">', $upcontext['custom_warning'], '</div>
4617
								</div>';
4618
4619
	echo '
4620
								<div class="righttext" style="margin: 1ex;">';
4621
4622
	if (!empty($upcontext['continue']))
4623
		echo '
4624
									<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '"', $upcontext['continue'] == 2 ? ' disabled' : '', ' class="button_submit">';
4625
	if (!empty($upcontext['skip']))
4626
		echo '
4627
									<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="dontSubmit = true; document.getElementById(\'contbutt\').disabled = \'disabled\'; return true;" class="button_submit">';
4628
4629
	echo '
4630
								</div>
4631
							</form>
4632
						</div>
4633
				</div>
4634
			</div>
4635
			</div>
4636
		</div>
4637
		</div>
4638
		<div id="footer">
4639
			<ul>
4640
				<li class="copyright"><a href="http://www.simplemachines.org/" title="Simple Machines Forum" target="_blank" class="new_win">SMF &copy; 2016, Simple Machines</a></li>
4641
			</ul>
4642
		</div>
4643
	</body>
4644
</html>';
4645
4646
	// Are we on a pause?
4647
	if (!empty($upcontext['pause']))
4648
	{
4649
		echo '
4650
		<script>
4651
			window.onload = doAutoSubmit;
4652
			var countdown = 3;
4653
			var dontSubmit = false;
4654
4655
			function doAutoSubmit()
4656
			{
4657
				if (countdown == 0 && !dontSubmit)
4658
					document.upform.submit();
4659
				else if (countdown == -1)
4660
					return;
4661
4662
				document.getElementById(\'contbutt\').value = "', $txt['upgrade_continue'], ' (" + countdown + ")";
4663
				countdown--;
4664
4665
				setTimeout("doAutoSubmit();", 1000);
4666
			}
4667
		</script>';
4668
	}
4669
}
4670
4671
function template_xml_above()
4672
{
4673
	global $upcontext;
4674
4675
	echo '<', '?xml version="1.0" encoding="UTF-8"?', '>
4676
	<smf>';
4677
4678
	if (!empty($upcontext['get_data']))
4679
		foreach ($upcontext['get_data'] as $k => $v)
4680
			echo '
4681
		<get key="', $k, '">', $v, '</get>';
4682
}
4683
4684
function template_xml_below()
4685
{
4686
	echo '
4687
		</smf>';
4688
}
4689
4690
function template_error_message()
4691
{
4692
	global $upcontext;
4693
4694
	echo '
4695
	<div class="error_message">
4696
		<div style="color: red;">
4697
			', $upcontext['error_msg'], '
4698
		</div>
4699
		<br>
4700
		<a href="', $_SERVER['PHP_SELF'], '">Click here to try again.</a>
4701
	</div>';
4702
}
4703
4704
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 (L2253-2302) 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...
4705
{
4706
	global $upcontext, $disable_security, $settings, $txt;
4707
4708
	echo '
4709
		<script src="http://www.simplemachines.org/smf/current-version.js?version=' . SMF_VERSION . '"></script>
4710
			<h3>', sprintf($txt['upgrade_ready_proceed'], SMF_VERSION), '</h3>
4711
	<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">
4712
		<input type="hidden" name="', $upcontext['login_token_var'], '" value="', $upcontext['login_token'], '">
4713
		<div id="version_warning" style="margin: 2ex; padding: 2ex; border: 2px dashed #a92174; color: black; background-color: #fbbbe2; display: none;">
4714
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4715
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
4716
			<div style="padding-left: 6ex;">
4717
				', sprintf($txt['upgrade_warning_out_of_date'], SMF_VERSION), '
4718
			</div>
4719
		</div>';
4720
4721
	$upcontext['chmod_in_form'] = true;
4722
	template_chmod();
4723
4724
	// For large, pre 1.1 RC2 forums give them a warning about the possible impact of this upgrade!
4725
	if ($upcontext['is_large_forum'])
4726
		echo '
4727
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
4728
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4729
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
4730
			<div style="padding-left: 6ex;">
4731
				', $txt['upgrade_warning_lots_data'], '
4732
			</div>
4733
		</div>';
4734
4735
	// A warning message?
4736
	if (!empty($upcontext['warning']))
4737
		echo '
4738
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
4739
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4740
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
4741
			<div style="padding-left: 6ex;">
4742
				', $upcontext['warning'], '
4743
			</div>
4744
		</div>';
4745
4746
	// Paths are incorrect?
4747
	echo '
4748
		<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">
4749
			<div style="float: left; width: 2ex; font-size: 2em; color: black;">!!</div>
4750
			<strong style="text-decoration: underline;">', $txt['upgrade_critical_error'], '</strong><br>
4751
			<div style="padding-left: 6ex;">
4752
				', $txt['upgrade_error_script_js'], '
4753
			</div>
4754
		</div>';
4755
4756
	// Is there someone already doing this?
4757
	if (!empty($upcontext['user']['id']) && (time() - $upcontext['started'] < 72600 || time() - $upcontext['updated'] < 3600))
4758
	{
4759
		$ago = time() - $upcontext['started'];
4760
		if ($ago < 60)
4761
			$ago = $ago . ' seconds';
4762
		elseif ($ago < 3600)
4763
			$ago = (int) ($ago / 60) . ' minutes';
4764
		else
4765
			$ago = (int) ($ago / 3600) . ' hours';
4766
4767
		$active = time() - $upcontext['updated'];
4768
		if ($active < 60)
4769
			$updated = $active . ' seconds';
4770
		elseif ($active < 3600)
4771
			$updated = (int) ($active / 60) . ' minutes';
4772
		else
4773
			$updated = (int) ($active / 3600) . ' hours';
4774
4775
		echo '
4776
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
4777
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4778
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
4779
			<div style="padding-left: 6ex;">
4780
				&quot;', $upcontext['user']['name'], '&quot; has been running the upgrade script for the last ', $ago, ' - and was last active ', $updated, ' ago.';
4781
4782
		if ($active < 600)
4783
			echo '
4784
				We recommend that you do not run this script unless you are sure that ', $upcontext['user']['name'], ' has completed their upgrade.';
4785
4786
		if ($active > $upcontext['inactive_timeout'])
4787
			echo '
4788
				<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.';
4789
		else
4790
			echo '
4791
				<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!');
4792
4793
		echo '
4794
			</div>
4795
		</div>';
4796
	}
4797
4798
	echo '
4799
			<strong>Admin Login: ', $disable_security ? '(DISABLED)' : '', '</strong>
4800
			<h3>For security purposes please login with your admin account to proceed with the upgrade.</h3>
4801
			<table>
4802
				<tr valign="top">
4803
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Username:</strong></td>
4804
					<td>
4805
						<input type="text" name="user" value="', !empty($upcontext['username']) ? $upcontext['username'] : '', '"', $disable_security ? ' disabled' : '', ' class="input_text">';
4806
4807
	if (!empty($upcontext['username_incorrect']))
4808
		echo '
4809
						<div class="smalltext" style="color: red;">Username Incorrect</div>';
4810
4811
	echo '
4812
					</td>
4813
				</tr>
4814
				<tr valign="top">
4815
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Password:</strong></td>
4816
					<td>
4817
						<input type="password" name="passwrd" value=""', $disable_security ? ' disabled' : '', ' class="input_password">
4818
						<input type="hidden" name="hash_passwrd" value="">';
4819
4820
	if (!empty($upcontext['password_failed']))
4821
		echo '
4822
						<div class="smalltext" style="color: red;">Password Incorrect</div>';
4823
4824
	echo '
4825
					</td>
4826
				</tr>';
4827
4828
	// Can they continue?
4829
	if (!empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] >= $upcontext['inactive_timeout'] && $upcontext['user']['step'] > 1)
4830
	{
4831
		echo '
4832
				<tr>
4833
					<td colspan="2">
4834
						<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>
4835
					</td>
4836
				</tr>';
4837
	}
4838
4839
	echo '
4840
			</table><br>
4841
			<span class="smalltext">
4842
				<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.
4843
			</span>
4844
			<input type="hidden" name="login_attempt" id="login_attempt" value="1">
4845
			<input type="hidden" name="js_works" id="js_works" value="0">';
4846
4847
	// Say we want the continue button!
4848
	$upcontext['continue'] = !empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] < $upcontext['inactive_timeout'] ? 2 : 1;
4849
4850
	// This defines whether javascript is going to work elsewhere :D
4851
	echo '
4852
		<script>
4853
			if (\'XMLHttpRequest\' in window && document.getElementById(\'js_works\'))
4854
				document.getElementById(\'js_works\').value = 1;
4855
4856
			// Latest version?
4857
			function smfCurrentVersion()
4858
			{
4859
				var smfVer, yourVer;
4860
4861
				if (!(\'smfVersion\' in window))
4862
					return;
4863
4864
				window.smfVersion = window.smfVersion.replace(/SMF\s?/g, \'\');
4865
4866
				smfVer = document.getElementById(\'smfVersion\');
4867
				yourVer = document.getElementById(\'yourVersion\');
4868
4869
				setInnerHTML(smfVer, window.smfVersion);
4870
4871
				var currentVersion = getInnerHTML(yourVer);
4872
				if (currentVersion < window.smfVersion)
4873
					document.getElementById(\'version_warning\').style.display = \'\';
4874
			}
4875
			addLoadEvent(smfCurrentVersion);
4876
4877
			// This checks that the script file even exists!
4878
			if (typeof(smfSelectText) == \'undefined\')
4879
				document.getElementById(\'js_script_missing_error\').style.display = \'\';
4880
4881
		</script>';
4882
}
4883
4884
function template_upgrade_options()
4885
{
4886
	global $upcontext, $modSettings, $db_prefix, $mmessage, $mtitle, $db_type;
4887
4888
	echo '
4889
			<h3>Before the upgrade gets underway please review the options below - and hit continue when you\'re ready to begin.</h3>
4890
			<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">';
4891
4892
	// Warning message?
4893
	if (!empty($upcontext['upgrade_options_warning']))
4894
		echo '
4895
		<div style="margin: 1ex; padding: 1ex; border: 1px dashed #cc3344; color: black; background-color: #ffe4e9;">
4896
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4897
			<strong style="text-decoration: underline;">Warning!</strong><br>
4898
			<div style="padding-left: 4ex;">
4899
				', $upcontext['upgrade_options_warning'], '
4900
			</div>
4901
		</div>';
4902
4903
	echo '
4904
				<table>
4905
					<tr valign="top">
4906
						<td width="2%">
4907
							<input type="checkbox" name="backup" id="backup" value="1"', $db_type != 'mysql' && $db_type != 'mysqli' && $db_type != 'postgresql' ? ' disabled' : '', ' class="input_check">
4908
						</td>
4909
						<td width="100%">
4910
							<label for="backup">Backup tables in your database with the prefix &quot;backup_' . $db_prefix . '&quot;.</label>', isset($modSettings['smfVersion']) ? '' : ' (recommended!)', '
4911
						</td>
4912
					</tr>
4913
					<tr valign="top">
4914
						<td width="2%">
4915
							<input type="checkbox" name="maint" id="maint" value="1" checked class="input_check">
4916
						</td>
4917
						<td width="100%">
4918
							<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>
4919
							<div id="mainmess" style="display: none;">
4920
								<strong class="smalltext">Maintenance Title: </strong><br>
4921
								<input type="text" name="maintitle" size="30" value="', htmlspecialchars($mtitle), '" class="input_text"><br>
4922
								<strong class="smalltext">Maintenance Message: </strong><br>
4923
								<textarea name="mainmessage" rows="3" cols="50">', htmlspecialchars($mmessage), '</textarea>
4924
							</div>
4925
						</td>
4926
					</tr>';
4927
4928
	// Offer mysql users to switch to mysqli
4929
	if ($db_type == 'mysql' && function_exists('mysqli_query'))
4930
		echo '
4931
					<tr valign="top">
4932
						<td width="2%">
4933
							<input type="checkbox" name="convertMysql" id="convertMysql" value="1" checked class="input_check">
4934
						</td>
4935
						<td width="100%">
4936
							<label for="convertMysql">Use MySQLi functionality (MySQL compatible).</span>
4937
							<strong class="smalltext"><a href="http://wiki.simplemachines.org/smf/Upgrading-MySQLi-Functionality" target="_blank">More information about MySQLi</a></strong><br>
4938
						</td>
4939
					</tr>';
4940
4941
	echo '
4942
					<tr valign="top">
4943
						<td width="2%">
4944
							<input type="checkbox" name="debug" id="debug" value="1" class="input_check">
4945
						</td>
4946
						<td width="100%">
4947
							<label for="debug">Output extra debugging information</label>
4948
						</td>
4949
					</tr>
4950
					<tr valign="top">
4951
						<td width="2%">
4952
							<input type="checkbox" name="empty_error" id="empty_error" value="1" class="input_check">
4953
						</td>
4954
						<td width="100%">
4955
							<label for="empty_error">Empty error log before upgrading</label>
4956
						</td>
4957
					</tr>';
4958
4959
	if (!empty($upcontext['karma_installed']['good']) || !empty($upcontext['karma_installed']['bad']))
4960
		echo '
4961
					<tr valign="top">
4962
						<td width="2%">
4963
							<input type="checkbox" name="delete_karma" id="delete_karma" value="1" class="input_check">
4964
						</td>
4965
						<td width="100%">
4966
							<label for="delete_karma">Delete all karma settings and info from the DB</label>
4967
						</td>
4968
					</tr>';
4969
4970
	echo '
4971
					<tr valign="top">
4972
						<td width="2%">
4973
							<input type="checkbox" name="stat" id="stat" value="1"', empty($modSettings['allow_sm_stats']) ? '' : ' checked', ' class="input_check">
4974
						</td>
4975
						<td width="100%">
4976
							<label for="stat">
4977
								Allow Simple Machines to Collect Basic Stats Monthly.<br>
4978
								<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>
4979
							</label>
4980
						</td>
4981
					</tr>
4982
				</table>
4983
				<input type="hidden" name="upcont" value="1">';
4984
4985
	// We need a normal continue button here!
4986
	$upcontext['continue'] = 1;
4987
}
4988
4989
// Template for the database backup tool/
4990
function template_backup_database()
4991
{
4992
	global $upcontext, $support_js, $is_debug;
4993
4994
	echo '
4995
			<h3>Please wait while a backup is created. For large forums this may take some time!</h3>';
4996
4997
	echo '
4998
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4999
			<input type="hidden" name="backup_done" id="backup_done" value="0">
5000
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
5001
			<span id="debuginfo"></span>';
5002
5003
	// Dont any tables so far?
5004
	if (!empty($upcontext['previous_tables']))
5005
		foreach ($upcontext['previous_tables'] as $table)
5006
			echo '
5007
			<br>Completed Table: &quot;', $table, '&quot;.';
5008
5009
	echo '
5010
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
5011
			<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>';
5012
5013
	// Continue please!
5014
	$upcontext['continue'] = $support_js ? 2 : 1;
5015
5016
	// If javascript allows we want to do this using XML.
5017
	if ($support_js)
5018
	{
5019
		echo '
5020
		<script>
5021
			var lastTable = ', $upcontext['cur_table_num'], ';
5022
			function getNextTables()
5023
			{
5024
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
5025
			}
5026
5027
			// Got an update!
5028
			function onBackupUpdate(oXMLDoc)
5029
			{
5030
				var sCurrentTableName = "";
5031
				var iTableNum = 0;
5032
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
5033
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
5034
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
5035
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
5036
5037
				// Update the page.
5038
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
5039
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
5040
				lastTable = iTableNum;
5041
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
5042
5043
		// If debug flood the screen.
5044
		if ($is_debug)
5045
			echo '
5046
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
5047
5048
				if (document.getElementById(\'debuginfo\').scrollHeight)
5049
					document.getElementById(\'debuginfo\').scrollTop = document.getElementById(\'debuginfo\').scrollHeight;';
5050
5051
		echo '
5052
				// Get the next update...
5053
				if (iTableNum == ', $upcontext['table_count'], ')
5054
				{
5055
					document.getElementById(\'commess\').style.display = "";
5056
					document.getElementById(\'current_tab_div\').style.display = "none";
5057
					document.getElementById(\'contbutt\').disabled = 0;
5058
					document.getElementById(\'backup_done\').value = 1;
5059
				}
5060
				else
5061
					getNextTables();
5062
			}
5063
			getNextTables();
5064
		</script>';
5065
	}
5066
}
5067
5068
function template_backup_xml()
5069
{
5070
	global $upcontext;
5071
5072
	echo '
5073
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
5074
}
5075
5076
// Here is the actual "make the changes" template!
5077
function template_database_changes()
5078
{
5079
	global $upcontext, $support_js, $is_debug, $timeLimitThreshold;
5080
5081
	if (empty($is_debug) && !empty($upcontext['upgrade_status']['debug']))
5082
		$is_debug = true;
5083
5084
	echo '
5085
		<h3>Executing database changes</h3>
5086
		<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>';
5087
5088
	echo '
5089
		<form action="', $upcontext['form_url'], '&amp;filecount=', $upcontext['file_count'], '" name="upform" id="upform" method="post">
5090
		<input type="hidden" name="database_done" id="database_done" value="0">';
5091
5092
	// No javascript looks rubbish!
5093
	if (!$support_js)
5094
	{
5095
		foreach ($upcontext['actioned_items'] as $num => $item)
5096
		{
5097
			if ($num != 0)
5098
				echo ' Successful!';
5099
			echo '<br>' . $item;
5100
		}
5101
		if (!empty($upcontext['changes_complete']))
5102
		{
5103
			$active = time() - $upcontext['started'];
5104
			$hours = floor($active / 3600);     
5105
			$minutes = intval(($active / 60) % 60);        
5106
			$seconds = intval($active % 60);     
5107
5108 View Code Duplication
			if ($is_debug)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
5109
			{
5110
				$totalTime = '';
5111
				if ($hours > 0)
5112
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's':'') . ' ';        
5113
				if ($minutes > 0)
5114
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's':'') . ' ';        
5115
				if ($seconds > 0)
5116
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's':'') . ' ';        
5117
			}
5118
5119
			if ($is_debug && !empty($totalTime))
5120
				echo ' Successful! Completed in ', $totalTime, '<br><br>';
5121
			else
5122
				echo ' Successful!<br><br>';
5123
5124
			echo '<span id="commess" style="font-weight: bold;">Database Updates Complete! Click Continue to Proceed.</span><br>';
5125
		}
5126
	}
5127
	else
5128
	{
5129
		// Tell them how many files we have in total.
5130
		if ($upcontext['file_count'] > 1)
5131
			echo '
5132
		<strong id="info1">Executing upgrade script <span id="file_done">', $upcontext['cur_file_num'], '</span> of ', $upcontext['file_count'], '.</strong>';
5133
5134
		echo '
5135
		<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>
5136
		<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>';
5137
5138
		if ($is_debug)
5139
		{
5140
			if ($upcontext['current_debug_item_num'] == $upcontext['debug_items'])
5141
			{
5142
				$totalTime = '';
5143
				if ($hours > 0)
0 ignored issues
show
Bug introduced by
The variable $hours seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
5144
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's':'') . ' ';        
0 ignored issues
show
Bug introduced by
The variable $hours seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
5145
				if ($minutes > 0)
0 ignored issues
show
Bug introduced by
The variable $minutes seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
5146
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's':'') . ' ';        
0 ignored issues
show
Bug introduced by
The variable $minutes seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
5147
				if ($seconds > 0)
0 ignored issues
show
Bug introduced by
The variable $seconds seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
5148
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's':'') . ' ';        
0 ignored issues
show
Bug introduced by
The variable $seconds seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
5149
			}
5150
5151
			if ($is_debug && !empty($totalTime))
5152
				echo '<br> Completed in ', $totalTime, '<br>';
5153
5154
			echo '
5155
			<div id="debug_section" style="height: 200px; overflow: auto;">
5156
			<span id="debuginfo"></span>
5157
			</div>';
5158
		}
5159
	}
5160
5161
	// Place for the XML error message.
5162
	echo '
5163
		<div id="error_block" style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9; display: ', empty($upcontext['error_message']) ? 'none' : '', ';">
5164
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
5165
			<strong style="text-decoration: underline;">Error!</strong><br>
5166
			<div style="padding-left: 6ex;" id="error_message">', isset($upcontext['error_message']) ? $upcontext['error_message'] : 'Unknown Error!', '</div>
5167
		</div>';
5168
5169
	// We want to continue at some point!
5170
	$upcontext['continue'] = $support_js ? 2 : 1;
5171
5172
	// If javascript allows we want to do this using XML.
5173
	if ($support_js)
5174
	{
5175
		echo '
5176
		<script>
5177
			var lastItem = ', $upcontext['current_debug_item_num'], ';
5178
			var sLastString = "', strtr($upcontext['current_debug_item_name'], array('"' => '&quot;')), '";
5179
			var iLastSubStepProgress = -1;
5180
			var curFile = ', $upcontext['cur_file_num'], ';
5181
			var totalItems = 0;
5182
			var prevFile = 0;
5183
			var retryCount = 0;
5184
			var testvar = 0;
5185
			var timeOutID = 0;
5186
			var getData = "";
5187
			var debugItems = ', $upcontext['debug_items'], ';
5188
			function getNextItem()
5189
			{
5190
				// We want to track this...
5191
				if (timeOutID)
5192
					clearTimeout(timeOutID);
5193
				timeOutID = window.setTimeout("retTimeout()", ', (10 * $timeLimitThreshold), '000);
5194
5195
				getXMLDocument(\'', $upcontext['form_url'], '&xml&filecount=', $upcontext['file_count'], '&substep=\' + lastItem + getData, onItemUpdate);
5196
			}
5197
5198
			// Got an update!
5199
			function onItemUpdate(oXMLDoc)
5200
			{
5201
				var sItemName = "";
5202
				var sDebugName = "";
5203
				var iItemNum = 0;
5204
				var iSubStepProgress = -1;
5205
				var iDebugNum = 0;
5206
				var bIsComplete = 0;
5207
				getData = "";
5208
5209
				// We\'ve got something - so reset the timeout!
5210
				if (timeOutID)
5211
					clearTimeout(timeOutID);
5212
5213
				// Assume no error at this time...
5214
				document.getElementById("error_block").style.display = "none";
5215
5216
				// Are we getting some duff info?
5217
				if (!oXMLDoc.getElementsByTagName("item")[0])
5218
				{
5219
					// Too many errors?
5220
					if (retryCount > 15)
5221
					{
5222
						document.getElementById("error_block").style.display = "";
5223
						setInnerHTML(document.getElementById("error_message"), "Error retrieving information on step: " + (sDebugName == "" ? sLastString : sDebugName));';
5224
5225
	if ($is_debug)
5226
		echo '
5227
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
5228
5229
	echo '
5230
					}
5231
					else
5232
					{
5233
						retryCount++;
5234
						getNextItem();
5235
					}
5236
					return false;
5237
				}
5238
5239
				// Never allow loops.
5240
				if (curFile == prevFile)
5241
				{
5242
					retryCount++;
5243
					if (retryCount > 10)
5244
					{
5245
						document.getElementById("error_block").style.display = "";
5246
						setInnerHTML(document.getElementById("error_message"), "Upgrade script appears to be going into a loop - step: " + sDebugName);';
5247
5248
	if ($is_debug)
5249
		echo '
5250
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
5251
5252
	echo '
5253
					}
5254
				}
5255
				retryCount = 0;
5256
5257
				for (var i = 0; i < oXMLDoc.getElementsByTagName("item")[0].childNodes.length; i++)
5258
					sItemName += oXMLDoc.getElementsByTagName("item")[0].childNodes[i].nodeValue;
5259
				for (var i = 0; i < oXMLDoc.getElementsByTagName("debug")[0].childNodes.length; i++)
5260
					sDebugName += oXMLDoc.getElementsByTagName("debug")[0].childNodes[i].nodeValue;
5261
				for (var i = 0; i < oXMLDoc.getElementsByTagName("get").length; i++)
5262
				{
5263
					getData += "&" + oXMLDoc.getElementsByTagName("get")[i].getAttribute("key") + "=";
5264
					for (var j = 0; j < oXMLDoc.getElementsByTagName("get")[i].childNodes.length; j++)
5265
					{
5266
						getData += oXMLDoc.getElementsByTagName("get")[i].childNodes[j].nodeValue;
5267
					}
5268
				}
5269
5270
				iItemNum = oXMLDoc.getElementsByTagName("item")[0].getAttribute("num");
5271
				iDebugNum = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("num"));
5272
				bIsComplete = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("complete"));
5273
				iSubStepProgress = parseFloat(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("percent"));
5274
				sLastString = sDebugName + " (Item: " + iDebugNum + ")";
5275
5276
				curFile = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("num"));
5277
				debugItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("debug_items"));
5278
				totalItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("items"));
5279
5280
				// If we have an error we haven\'t completed!
5281
				if (oXMLDoc.getElementsByTagName("error")[0] && bIsComplete)
5282
					iDebugNum = lastItem;
5283
5284
				// Do we have the additional progress bar?
5285
				if (iSubStepProgress != -1)
5286
				{
5287
					document.getElementById("substep_bar_div").style.display = "";
5288
					document.getElementById("substep_bar_div2").style.display = "";
5289
					document.getElementById("substep_progress").style.width = iSubStepProgress + "%";
5290
					setInnerHTML(document.getElementById("substep_text"), iSubStepProgress + "%");
5291
					setInnerHTML(document.getElementById("substep_bar_div"), sDebugName.replace(/\./g, "") + ":");
5292
				}
5293
				else
5294
				{
5295
					document.getElementById("substep_bar_div").style.display = "none";
5296
					document.getElementById("substep_bar_div2").style.display = "none";
5297
				}
5298
5299
				// Move onto the next item?
5300
				if (bIsComplete)
5301
					lastItem = iDebugNum;
5302
				else
5303
					lastItem = iDebugNum - 1;
5304
5305
				// Are we finished?
5306
				if (bIsComplete && iDebugNum == -1 && curFile >= ', $upcontext['file_count'], ')
5307
				{';
5308
5309
		if ($is_debug)
5310
			echo '
5311
					document.getElementById(\'debug_section\').style.display = "none";';
5312
5313
		echo '
5314
5315
					document.getElementById(\'commess\').style.display = "";
5316
					document.getElementById(\'contbutt\').disabled = 0;
5317
					document.getElementById(\'database_done\').value = 1;';
5318
5319
		if ($upcontext['file_count'] > 1)
5320
			echo '
5321
					document.getElementById(\'info1\').style.display = "none";';
5322
5323
		echo '
5324
					document.getElementById(\'info2\').style.display = "none";
5325
					updateStepProgress(100, 100, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
5326
					return true;
5327
				}
5328
				// Was it the last step in the file?
5329
				else if (bIsComplete && iDebugNum == -1)
5330
				{
5331
					lastItem = 0;
5332
					prevFile = curFile;';
5333
5334
		if ($is_debug)
5335
			echo '
5336
					setOuterHTML(document.getElementById(\'debuginfo\'), \'Moving to next script file...done<br><span id="debuginfo"><\' + \'/span>\');';
5337
5338
		echo '
5339
					getNextItem();
5340
					return true;
5341
				}';
5342
5343
		// If debug scroll the screen.
5344
		if ($is_debug)
5345
			echo '
5346
				if (iLastSubStepProgress == -1)
5347
				{
5348
					// Give it consistent dots.
5349
					dots = sDebugName.match(/\./g);
5350
					numDots = dots ? dots.length : 0;
5351
					for (var i = numDots; i < 3; i++)
5352
						sDebugName += ".";
5353
					setOuterHTML(document.getElementById(\'debuginfo\'), sDebugName + \'<span id="debuginfo"><\' + \'/span>\');
5354
				}
5355
				iLastSubStepProgress = iSubStepProgress;
5356
5357
				if (bIsComplete)
5358
					setOuterHTML(document.getElementById(\'debuginfo\'), \'done<br><span id="debuginfo"><\' + \'/span>\');
5359
				else
5360
					setOuterHTML(document.getElementById(\'debuginfo\'), \'...<span id="debuginfo"><\' + \'/span>\');
5361
5362
				if (document.getElementById(\'debug_section\').scrollHeight)
5363
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
5364
5365
		echo '
5366
				// Update the page.
5367
				setInnerHTML(document.getElementById(\'item_num\'), iItemNum);
5368
				setInnerHTML(document.getElementById(\'cur_item_name\'), sItemName);';
5369
5370
		if ($upcontext['file_count'] > 1)
5371
		{
5372
			echo '
5373
				setInnerHTML(document.getElementById(\'file_done\'), curFile);
5374
				setInnerHTML(document.getElementById(\'item_count\'), totalItems);';
5375
		}
5376
5377
		echo '
5378
				// Is there an error?
5379
				if (oXMLDoc.getElementsByTagName("error")[0])
5380
				{
5381
					var sErrorMsg = "";
5382
					for (var i = 0; i < oXMLDoc.getElementsByTagName("error")[0].childNodes.length; i++)
5383
						sErrorMsg += oXMLDoc.getElementsByTagName("error")[0].childNodes[i].nodeValue;
5384
					document.getElementById("error_block").style.display = "";
5385
					setInnerHTML(document.getElementById("error_message"), sErrorMsg);
5386
					return false;
5387
				}
5388
5389
				// Get the progress bar right.
5390
				barTotal = debugItems * ', $upcontext['file_count'], ';
5391
				barDone = (debugItems * (curFile - 1)) + lastItem;
5392
5393
				updateStepProgress(barDone, barTotal, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
5394
5395
				// Finally - update the time here as it shows the server is responding!
5396
				curTime = new Date();
5397
				iElapsed = (curTime.getTime() / 1000 - ', $upcontext['started'], ');
5398
				mins = parseInt(iElapsed / 60);
5399
				secs = parseInt(iElapsed - mins * 60);
5400
				setInnerHTML(document.getElementById("mins_elapsed"), mins);
5401
				setInnerHTML(document.getElementById("secs_elapsed"), secs);
5402
5403
				getNextItem();
5404
				return true;
5405
			}
5406
5407
			// What if we timeout?!
5408
			function retTimeout(attemptAgain)
5409
			{
5410
				// Oh noes...
5411
				if (!attemptAgain)
5412
				{
5413
					document.getElementById("error_block").style.display = "";
5414
					setInnerHTML(document.getElementById("error_message"), "Server has not responded for ', ($timeLimitThreshold * 10), ' seconds. It may be worth waiting a little longer or otherwise please click <a href=\"#\" onclick=\"retTimeout(true); return false;\">here<" + "/a> to try this step again");
5415
				}
5416
				else
5417
				{
5418
					document.getElementById("error_block").style.display = "none";
5419
					getNextItem();
5420
				}
5421
			}';
5422
5423
		// Start things off assuming we've not errored.
5424
		if (empty($upcontext['error_message']))
5425
			echo '
5426
			getNextItem();';
5427
5428
		echo '
5429
		</script>';
5430
	}
5431
	return;
5432
}
5433
5434
function template_database_xml()
5435
{
5436
	global $upcontext, $txt;
5437
5438
	echo '
5439
	<file num="', $upcontext['cur_file_num'], '" items="', $upcontext['total_items'], '" debug_items="', $upcontext['debug_items'], '">', $upcontext['cur_file_name'], '</file>
5440
	<item num="', $upcontext['current_item_num'], '">', $upcontext['current_item_name'], '</item>
5441
	<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>';
5442
5443
	if (!empty($upcontext['error_message']))
5444
		echo '
5445
	<error>', $upcontext['error_message'], '</error>';
5446
5447
	if (!empty($upcontext['error_string']))
5448
		echo '
5449
	<sql>', $upcontext['error_string'], '</sql>';
5450
}
5451
5452
// Template for the UTF-8 conversion step. Basically a copy of the backup stuff with slight modifications....
5453 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...
5454
{
5455
	global $upcontext, $support_js, $is_debug;
5456
5457
	echo '
5458
			<h3>Please wait while your database is converted to UTF-8. For large forums this may take some time!</h3>';
5459
5460
	echo '
5461
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
5462
			<input type="hidden" name="utf8_done" id="utf8_done" value="0">
5463
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
5464
			<span id="debuginfo"></span>';
5465
5466
	// Done any tables so far?
5467
	if (!empty($upcontext['previous_tables']))
5468
		foreach ($upcontext['previous_tables'] as $table)
5469
			echo '
5470
			<br>Completed Table: &quot;', $table, '&quot;.';
5471
5472
	echo '
5473
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>';
5474
5475
	// If we dropped their index, let's let them know
5476
	if ($upcontext['cur_table_num'] == $upcontext['table_count'] && $upcontext['dropping_index'])
5477
		echo '
5478
			<br><span style="display:inline;">Please note that your fulltext index was dropped to facilitate the conversion and will need to be recreated.</span>';
5479
5480
	echo '
5481
			<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>';
5482
5483
	// Continue please!
5484
	$upcontext['continue'] = $support_js ? 2 : 1;
5485
5486
	// If javascript allows we want to do this using XML.
5487
	if ($support_js)
5488
	{
5489
		echo '
5490
		<script>
5491
			var lastTable = ', $upcontext['cur_table_num'], ';
5492
			function getNextTables()
5493
			{
5494
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
5495
			}
5496
5497
			// Got an update!
5498
			function onBackupUpdate(oXMLDoc)
5499
			{
5500
				var sCurrentTableName = "";
5501
				var iTableNum = 0;
5502
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
5503
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
5504
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
5505
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
5506
5507
				// Update the page.
5508
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
5509
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
5510
				lastTable = iTableNum;
5511
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
5512
5513
		// If debug flood the screen.
5514
		if ($is_debug)
5515
			echo '
5516
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');';
5517
5518
		echo '
5519
				// Get the next update...
5520
				if (iTableNum == ', $upcontext['table_count'], ')
5521
				{
5522
					document.getElementById(\'commess\').style.display = "";
5523
					document.getElementById(\'current_tab_div\').style.display = "none";
5524
					document.getElementById(\'contbutt\').disabled = 0;
5525
					document.getElementById(\'utf8_done\').value = 1;
5526
				}
5527
				else
5528
					getNextTables();
5529
			}
5530
			getNextTables();
5531
		</script>';
5532
	}
5533
}
5534
5535
function template_utf8_xml()
5536
{
5537
	global $upcontext;
5538
5539
	echo '
5540
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
5541
}
5542
5543
function template_clean_mods()
5544
{
5545
	global $upcontext;
5546
5547
	$upcontext['chmod_in_form'] = true;
5548
5549
	echo '
5550
	<h3>SMF has detected some packages which were installed but not fully removed prior to upgrade. We recommend you remove the following mods and reinstall upon completion of the upgrade.</h3>
5551
	<form action="', $upcontext['form_url'], '&amp;ssi=1" name="upform" id="upform" method="post">';
5552
5553
	// In case it's required.
5554
	template_chmod();
5555
5556
	echo '
5557
		<table width="90%" align="center" style="background-color: black;">
5558
			<tr style="background-color: #eeeeee;">
5559
				<td width="40%"><strong>Modification Name</strong></td>
5560
				<td width="10%" align="center"><strong>Version</strong></td>
5561
				<td width="15%"><strong>Files Affected</strong></td>
5562
				<td width="20%"><strong>Status</strong></td>
5563
				<td width="5%" align="center"><strong>Fix?</strong></td>
5564
			</tr>';
5565
5566
	foreach ($upcontext['packages'] as $package)
5567
	{
5568
		echo '
5569
			<tr style="background-color: #cccccc;">
5570
				<td width="40%">', $package['name'], '</td>
5571
				<td width="10%">', $package['version'], '</td>
5572
				<td width="15%">', $package['file_count'], ' <span class="smalltext">[<a href="#" onclick="alert(\'The following files are affected by this modification:\\n\\n', strtr(implode('<br>', $package['files']), array('\\' => '\\\\', '<br>' => '\\n')), '\'); return false;">details</a>]</td>
5573
				<td width="20%"><span style="font-weight: bold; color: ', $package['color'], '">', $package['status'], '</span></td>
5574
				<td width="5%" align="center">
5575
					<input type="hidden" name="remove[', $package['id'], ']" value="0">
5576
					<input type="checkbox" name="remove[', $package['id'], ']"', $package['color'] == 'green' ? ' disabled' : '', ' class="input_check">
5577
				</td>
5578
			</tr>';
5579
	}
5580
	echo '
5581
		</table>
5582
		<input type="hidden" name="cleandone" value="1">';
5583
5584
	// Files to make writable?
5585 View Code Duplication
	if (!empty($upcontext['writable_files']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
5586
		echo '
5587
		<input type="hidden" name="writable_files" value="', base64_encode(safe_serialize($upcontext['writable_files'])), '">';
5588
5589
	// We'll want a continue button...
5590
	if (empty($upcontext['chmod']['files']))
5591
		$upcontext['continue'] = 1;
5592
}
5593
5594
// Finished with the mods - let them know what we've done.
5595
function template_cleanup_done()
5596
{
5597
	global $upcontext;
5598
5599
	echo '
5600
	<h3>SMF has attempted to fix and reinstall mods as required. We recommend you visit the package manager upon completing upgrade to check the status of your modifications.</h3>
5601
	<form action="', $upcontext['form_url'], '&amp;ssi=1" name="upform" id="upform" method="post">
5602
		<table width="90%" align="center" style="background-color: black;">
5603
			<tr style="background-color: #eeeeee;">
5604
				<td width="100%"><strong>Actions Completed:</strong></td>
5605
			</tr>';
5606
5607
	foreach ($upcontext['packages'] as $package)
5608
	{
5609
		echo '
5610
			<tr style="background-color: #cccccc;">
5611
				<td>', $package['name'], '... <span style="font-weight: bold; color: ', $package['color'], ';">', $package['result'], '</span></td>
5612
			</tr>';
5613
	}
5614
	echo '
5615
		</table>
5616
		<input type="hidden" name="cleandone2" value="1">';
5617
5618
	// We'll want a continue button...
5619
	$upcontext['continue'] = 1;
5620
}
5621
5622
// Do they want to upgrade their templates?
5623
function template_upgrade_templates()
5624
{
5625
	global $upcontext;
5626
5627
	echo '
5628
	<h3>There have been numerous language and template changes since the previous version of SMF. On this step the upgrader can attempt to automatically make these changes in your templates to save you from doing so manually.</h3>
5629
	<form action="', $upcontext['form_url'], '&amp;ssi=1', $upcontext['is_test'] ? '' : ';forreal=1', '" name="upform" id="upform" method="post">';
5630
5631
	// Any files need to be writable?
5632
	$upcontext['chmod_in_form'] = true;
5633
	template_chmod();
5634
5635
	// Language/Template files need an update?
5636
	if ($upcontext['temp_progress'] == 0 && !$upcontext['is_test'] && (!empty($upcontext['languages']) || !empty($upcontext['themes'])))
5637
	{
5638
		echo '
5639
		The following template files will be updated to ensure they are compatible with this version of SMF. Note that this can only fix a limited number of compatibility issues and in general you should seek out the latest version of these themes/language files.
5640
		<table width="90%" align="center" style="background-color: black;">
5641
			<tr style="background-color: #eeeeee;">
5642
				<td width="80%"><strong>Area</strong></td>
5643
				<td width="20%" align="center"><strong>Changes Required</strong></td>
5644
			</tr>';
5645
5646 View Code Duplication
		foreach ($upcontext['languages'] as $language)
0 ignored issues
show
Bug introduced by
The expression $upcontext['languages'] of type boolean is not traversable.
Loading history...
Duplication introduced by
This code seems to be duplicated across 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...
5647
		{
5648
			echo '
5649
				<tr style="background-color: #cccccc;">
5650
					<td width="80%">
5651
						&quot;', $language['name'], '&quot; Language Pack
5652
						<div class="smalltext">(';
5653
5654
			foreach ($language['files'] as $k => $file)
5655
				echo $file['name'], $k + 1 != count($language['files']) ? ', ' : ')';
5656
5657
			echo '
5658
						</div>
5659
					</td>
5660
					<td width="20%" align="center">', $language['edit_count'] == 0 ? 1 : $language['edit_count'], '</td>
5661
				</tr>';
5662
		}
5663
5664 View Code Duplication
		foreach ($upcontext['themes'] as $theme)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
5665
		{
5666
			echo '
5667
				<tr style="background-color: #CCCCCC;">
5668
					<td width="80%">
5669
						&quot;', $theme['name'], '&quot; Theme
5670
						<div class="smalltext">(';
5671
5672
			foreach ($theme['files'] as $k => $file)
5673
				echo $file['name'], $k + 1 != count($theme['files']) ? ', ' : ')';
5674
5675
			echo '
5676
						</div>
5677
					</td>
5678
					<td width="20%" align="center">', $theme['edit_count'] == 0 ? 1 : $theme['edit_count'], '</td>
5679
				</tr>';
5680
		}
5681
5682
		echo '
5683
		</table>';
5684
	}
5685
	else
5686
	{
5687
		$langFiles = 0;
5688
		$themeFiles = 0;
5689
		if (!empty($upcontext['languages']))
5690
			foreach ($upcontext['languages'] as $lang)
0 ignored issues
show
Bug introduced by
The expression $upcontext['languages'] of type boolean is not traversable.
Loading history...
5691
				$langFiles += count($lang['files']);
5692
		if (!empty($upcontext['themes']))
5693
			foreach ($upcontext['themes'] as $theme)
0 ignored issues
show
Bug introduced by
The expression $upcontext['themes'] of type boolean is not traversable.
Loading history...
5694
				$themeFiles += count($theme['files']);
5695
		echo sprintf('Found <strong>%d</strong> language files and <strong>%d</strong> templates requiring an update so far.', $langFiles, $themeFiles) . '<br>';
5696
5697
		// What we're currently doing?
5698
		if (!empty($upcontext['current_message']))
5699
			echo '
5700
				', $upcontext['current_message'];
5701
	}
5702
5703
	echo '
5704
		<input type="hidden" name="uptempdone" value="1">';
5705
5706 View Code Duplication
	if (!empty($upcontext['languages']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
5707
		echo '
5708
		<input type="hidden" name="languages" value="', base64_encode(safe_serialize($upcontext['languages'])), '">';
5709 View Code Duplication
	if (!empty($upcontext['themes']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
5710
		echo '
5711
		<input type="hidden" name="themes" value="', base64_encode(safe_serialize($upcontext['themes'])), '">';
5712 View Code Duplication
	if (!empty($upcontext['writable_files']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
5713
		echo '
5714
		<input type="hidden" name="writable_files" value="', base64_encode(safe_serialize($upcontext['writable_files'])), '">';
5715
5716
	// Offer them the option to upgrade from YaBB SE?
5717
	if (!empty($upcontext['can_upgrade_yabbse']))
5718
		echo '
5719
		<br><label for="conv"><input type="checkbox" name="conv" id="conv" value="1" class="input_check"> Convert the existing YaBB SE template and set it as default.</label><br>';
5720
5721
	// We'll want a continue button... assuming chmod is OK (Otherwise let them use connect!)
5722
	if (empty($upcontext['chmod']['files']) || $upcontext['is_test'])
5723
		$upcontext['continue'] = 1;
5724
}
5725
5726
// Template for the database backup tool/
5727 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...
5728
{
5729
	global $upcontext, $support_js, $is_debug;
5730
5731
	echo '
5732
			<h3>Converting data from serialize to JSON...</h3>';
5733
5734
	echo '
5735
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
5736
			<input type="hidden" name="json_done" id="json_done" value="0">
5737
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
5738
			<span id="debuginfo"></span>';
5739
5740
	// Dont any tables so far?
5741
	if (!empty($upcontext['previous_tables']))
5742
		foreach ($upcontext['previous_tables'] as $table)
5743
			echo '
5744
			<br>Completed Table: &quot;', $table, '&quot;.';
5745
5746
	echo '
5747
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
5748
			<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>';
5749
5750
	// Try to make sure substep was reset.
5751
	if ($upcontext['cur_table_num'] == $upcontext['table_count'])
5752
		echo '
5753
			<input type="hidden" name="substep" id="substep" value="0">';
5754
5755
	// Continue please!
5756
	$upcontext['continue'] = $support_js ? 2 : 1;
5757
5758
	// If javascript allows we want to do this using XML.
5759
	if ($support_js)
5760
	{
5761
		echo '
5762
		<script>
5763
			var lastTable = ', $upcontext['cur_table_num'], ';
5764
			function getNextTables()
5765
			{
5766
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
5767
			}
5768
5769
			// Got an update!
5770
			function onBackupUpdate(oXMLDoc)
5771
			{
5772
				var sCurrentTableName = "";
5773
				var iTableNum = 0;
5774
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
5775
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
5776
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
5777
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
5778
5779
				// Update the page.
5780
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
5781
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
5782
				lastTable = iTableNum;
5783
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
5784
5785
		// If debug flood the screen.
5786
		if ($is_debug)
5787
			echo '
5788
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');';
5789
5790
		echo '
5791
				// Get the next update...
5792
				if (iTableNum == ', $upcontext['table_count'], ')
5793
				{
5794
					document.getElementById(\'commess\').style.display = "";
5795
					document.getElementById(\'current_tab_div\').style.display = "none";
5796
					document.getElementById(\'contbutt\').disabled = 0;
5797
					document.getElementById(\'json_done\').value = 1;
5798
				}
5799
				else
5800
					getNextTables();
5801
			}
5802
			getNextTables();
5803
		</script>';
5804
	}
5805
}
5806
5807
function template_serialize_json_xml()
5808
{
5809
	global $upcontext;
5810
5811
	echo '
5812
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
5813
}
5814
5815
function template_upgrade_complete()
5816
{
5817
	global $upcontext, $upgradeurl, $settings, $boardurl, $is_debug;
5818
5819
	echo '
5820
	<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>
5821
	<form action="', $boardurl, '/index.php">';
5822
5823
	if (!empty($upcontext['can_delete_script']))
5824
		echo '
5825
			<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>
5826
			<script>
5827
				function doTheDelete(theCheck)
5828
				{
5829
					var theImage = document.getElementById ? document.getElementById("delete_upgrader") : document.all.delete_upgrader;
5830
5831
					theImage.src = "', $upgradeurl, '?delete=1&ts_" + (new Date().getTime());
5832
					theCheck.disabled = true;
5833
				}
5834
			</script>
5835
			<img src="', $settings['default_theme_url'], '/images/blank.png" alt="" id="delete_upgrader"><br>';
5836
5837
	$active = time() - $upcontext['started'];
5838
	$hours = floor($active / 3600);     
5839
	$minutes = intval(($active / 60) % 60);        
5840
	$seconds = intval($active % 60);     
5841
5842 View Code Duplication
	if ($is_debug)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
5843
	{
5844
		$totalTime = '';
5845
		if ($hours > 0)
5846
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's':'') . ' ';        
5847
		if ($minutes > 0)
5848
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's':'') . ' ';        
5849
		if ($seconds > 0)
5850
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's':'') . ' ';        
5851
	}
5852
5853
	if ($is_debug && !empty($totalTime))
5854
		echo '<br> Upgrade completed in ', $totalTime, '<br><br>';
5855
5856
	echo '<br>
5857
			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>
5858
			<br>
5859
			Best of luck,<br>
5860
			Simple Machines';
5861
}
5862
5863
/**
5864
 * Convert MySQL (var)char ip col to binary
5865
 * newCol needs to be a varbinary(16) null able field
5866
 * return true or false
5867
 */
5868
function MySQLConvertOldIp($targetTable, $oldCol, $newCol, $limit = 50000)
5869
{
5870
	global $smcFunc;
5871
5872
	// Skip this if we don't have the column
5873
	$request = $smcFunc['db_query']('', '
5874
		SHOW FIELDS
5875
		FROM {db_prefix}{raw:table}
5876
		WHERE Field = {string:name}',
5877
		array(
5878
			'table' => $targetTable,
5879
			'name' => $oldCol,
5880
	));
5881
	if ($smcFunc['db_num_rows']($request) !== 1)
5882
	{
5883
		$smcFunc['db_free_result']($request);
5884
		return;
5885
	}
5886
	$smcFunc['db_free_result']($request);
5887
5888
	//mysql default max length is 1mb http://dev.mysql.com/doc/refman/5.1/en/packet-too-large.html
5889
	$arIp = array();
5890
5891
	$is_done = false;
5892
	while (!$is_done)
5893
	{
5894
		nextSubStep($substep);
0 ignored issues
show
Bug introduced by
The variable $substep does not exist. Did you forget to declare it?

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

Loading history...
5895
5896
		$request = $smcFunc['db_query']('', '
5897
			SELECT DISTINCT {raw:old_col}
5898
			FROM {db_prefix}{raw:table_name}
5899
			WHERE {raw:new_col} IS NULL
5900
			LIMIT {int:limit}',
5901
			array(
5902
				'old_col' => $oldCol,
5903
				'new_col' => $newCol,
5904
				'table_name' => $targetTable,
5905
				'empty' => '',
5906
				'limit' => $limit,
5907
		));
5908
		while ($row = $smcFunc['db_fetch_assoc']($request))
5909
			$arIp[] = $row[$oldCol]; 
5910
		$smcFunc['db_free_result']($request);
5911
5912
		// Special case, null ip could keep us in a loop.
5913
		if (is_null($arIp[0]))
5914
			unset($arIp[0]);
5915
5916
		if (empty($arIp))
5917
			$is_done = true;
5918
5919
		for ($i = 0; $i < count($arIp); $i++)
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
5920
			$request = $smcFunc['db_query']('', '
0 ignored issues
show
Unused Code introduced by
$request 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...
5921
				UPDATE {db_prefix}'.$targetTable.'
5922
				SET '.$newCol.' = {inet:ip'.$x.'}
0 ignored issues
show
Bug introduced by
The variable $x does not exist. Did you forget to declare it?

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

Loading history...
5923
				WHERE '.$oldCol.' = {string:ip'.$x.'}',
5924
				array(
5925
					'ip'.$x => trim($arIp[$i])
5926
				)
5927
			);
5928
5929
		$_GET['a'] += $limit;
5930
		$step_progress['current'] = $_GET['a'];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$step_progress was never initialized. Although not strictly required by PHP, it is generally a good practice to add $step_progress = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
5931
	}
5932
5933
	unset($_GET['a']);
5934
}
5935
5936
?>
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...