Completed
Push — release-2.1 ( 5876a2...f30e04 )
by Mert
06:50
created

upgrade.php ➔ template_database_changes()   F

Complexity

Conditions 22
Paths 5140

Size

Total Lines 316
Code Lines 64

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 22
eloc 64
nc 5140
nop 0
dl 0
loc 316
rs 2

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines http://www.simplemachines.org
8
 * @copyright 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
115
// Nothing sensible?
116
if (empty($upcontext['updated']))
117
{
118
	$upcontext['started'] = time();
119
	$upcontext['updated'] = 0;
120
	$upcontext['user'] = array(
121
		'id' => 0,
122
		'name' => 'Guest',
123
		'pass' => 0,
124
		'started' => $upcontext['started'],
125
		'updated' => $upcontext['updated'],
126
	);
127
}
128
129
// Load up some essential data...
130
loadEssentialData();
131
132
// Are we going to be mimic'ing SSI at this point?
133
if (isset($_GET['ssi']))
134
{
135
	require_once($sourcedir . '/Errors.php');
136
	require_once($sourcedir . '/Logging.php');
137
	require_once($sourcedir . '/Load.php');
138
	require_once($sourcedir . '/Security.php');
139
	require_once($sourcedir . '/Subs-Package.php');
140
141
	loadUserSettings();
142
	loadPermissions();
143
}
144
145
if (!function_exists('un_htmlspecialchars'))
146
{
147
	function un_htmlspecialchars($string)
148
	{
149
		return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, ENT_QUOTES)) + array('&#039;' => '\'', '&nbsp;' => ' '));
150
	}
151
}
152
153
if (!function_exists('text2words'))
154
{
155
	function text2words($text)
156
	{
157
		// Step 1: Remove entities/things we don't consider words:
158
		$words = preg_replace('~(?:[\x0B\0\xA0\t\r\s\n(){}\\[\\]<>!@$%^*.,:+=`\~\?/\\\\]+|&(?:amp|lt|gt|quot);)+~', ' ', $text);
159
160
		// Step 2: Entities we left to letters, where applicable, lowercase.
161
		$words = preg_replace('~([^&\d]|^)[#;]~', '$1 ', un_htmlspecialchars(strtolower($words)));
162
163
		// Step 3: Ready to split apart and index!
164
		$words = explode(' ', $words);
165
		$returned_words = array();
166
		foreach ($words as $word)
167
		{
168
			$word = trim($word, '-_\'');
169
170
			if ($word != '')
171
				$returned_words[] = substr($word, 0, 20);
172
		}
173
174
		return array_unique($returned_words);
175
	}
176
}
177
178
if (!function_exists('clean_cache'))
179
{
180
	// Empty out the cache folder.
181
	function clean_cache($type = '')
182
	{
183
		global $cachedir, $sourcedir;
184
185
		// No directory = no game.
186
		if (!is_dir($cachedir))
187
			return;
188
189
		// Remove the files in SMF's own disk cache, if any
190
		$dh = opendir($cachedir);
191 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...
192
		{
193
			if ($file != '.' && $file != '..' && $file != 'index.php' && $file != '.htaccess' && (!$type || substr($file, 0, strlen($type)) == $type))
194
				@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...
195
		}
196
		closedir($dh);
197
198
		// Invalidate cache, to be sure!
199
		// ... as long as index.php can be modified, anyway.
200
		@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...
201
		clearstatcache();
202
	}
203
}
204
205
// MD5 Encryption.
206
if (!function_exists('md5_hmac'))
207
{
208
	function md5_hmac($data, $key)
209
	{
210
		if (strlen($key) > 64)
211
			$key = pack('H*', md5($key));
212
		$key = str_pad($key, 64, chr(0x00));
213
214
		$k_ipad = $key ^ str_repeat(chr(0x36), 64);
215
		$k_opad = $key ^ str_repeat(chr(0x5c), 64);
216
217
		return md5($k_opad . pack('H*', md5($k_ipad . $data)));
218
	}
219
}
220
221
// http://www.faqs.org/rfcs/rfc959.html
222
if (!class_exists('ftp_connection'))
223
{
224
	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...
225
	{
226
		var $connection = 'no_connection', $error = false, $last_message, $pasv = array();
227
228
		// Create a new FTP connection...
229
		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...
230
		{
231
			if ($ftp_server !== null)
232
				$this->connect($ftp_server, $ftp_port, $ftp_user, $ftp_pass);
233
		}
234
235 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...
236
		{
237
			if (substr($ftp_server, 0, 6) == 'ftp://')
238
				$ftp_server = substr($ftp_server, 6);
239
			elseif (substr($ftp_server, 0, 7) == 'ftps://')
240
				$ftp_server = 'ssl://' . substr($ftp_server, 7);
241
			if (substr($ftp_server, 0, 7) == 'http://')
242
				$ftp_server = substr($ftp_server, 7);
243
			$ftp_server = strtr($ftp_server, array('/' => '', ':' => '', '@' => ''));
244
245
			// Connect to the FTP server.
246
			$this->connection = @fsockopen($ftp_server, $ftp_port, $err, $err, 5);
247
			if (!$this->connection)
248
			{
249
				$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...
250
				return;
251
			}
252
253
			// Get the welcome message...
254
			if (!$this->check_response(220))
255
			{
256
				$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...
257
				return;
258
			}
259
260
			// Send the username, it should ask for a password.
261
			fwrite($this->connection, 'USER ' . $ftp_user . "\r\n");
262
			if (!$this->check_response(331))
263
			{
264
				$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...
265
				return;
266
			}
267
268
			// Now send the password... and hope it goes okay.
269
			fwrite($this->connection, 'PASS ' . $ftp_pass . "\r\n");
270
			if (!$this->check_response(230))
271
			{
272
				$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...
273
				return;
274
			}
275
		}
276
277 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...
278
		{
279
			if (!is_resource($this->connection))
280
				return false;
281
282
			// No slash on the end, please...
283
			if (substr($ftp_path, -1) == '/' && $ftp_path !== '/')
284
				$ftp_path = substr($ftp_path, 0, -1);
285
286
			fwrite($this->connection, 'CWD ' . $ftp_path . "\r\n");
287
			if (!$this->check_response(250))
288
			{
289
				$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...
290
				return false;
291
			}
292
293
			return true;
294
		}
295
296 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...
297
		{
298
			if (!is_resource($this->connection))
299
				return false;
300
301
			// Convert the chmod value from octal (0777) to text ("777").
302
			fwrite($this->connection, 'SITE CHMOD ' . decoct($chmod) . ' ' . $ftp_file . "\r\n");
303
			if (!$this->check_response(200))
304
			{
305
				$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...
306
				return false;
307
			}
308
309
			return true;
310
		}
311
312 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...
313
		{
314
			// We are actually connected, right?
315
			if (!is_resource($this->connection))
316
				return false;
317
318
			// Delete file X.
319
			fwrite($this->connection, 'DELE ' . $ftp_file . "\r\n");
320
			if (!$this->check_response(250))
321
			{
322
				fwrite($this->connection, 'RMD ' . $ftp_file . "\r\n");
323
324
				// Still no love?
325
				if (!$this->check_response(250))
326
				{
327
					$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...
328
					return false;
329
				}
330
			}
331
332
			return true;
333
		}
334
335 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...
336
		{
337
			// Wait for a response that isn't continued with -, but don't wait too long.
338
			$time = time();
339
			do
340
				$this->last_message = fgets($this->connection, 1024);
341
			while (substr($this->last_message, 3, 1) != ' ' && time() - $time < 5);
342
343
			// Was the desired response returned?
344
			return is_array($desired) ? in_array(substr($this->last_message, 0, 3), $desired) : substr($this->last_message, 0, 3) == $desired;
345
		}
346
347 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...
348
		{
349
			// We can't create a passive data connection without a primary one first being there.
350
			if (!is_resource($this->connection))
351
				return false;
352
353
			// Request a passive connection - this means, we'll talk to you, you don't talk to us.
354
			@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...
355
			$time = time();
356
			do
357
				$response = fgets($this->connection, 1024);
358
			while (substr($response, 3, 1) != ' ' && time() - $time < 5);
359
360
			// If it's not 227, we weren't given an IP and port, which means it failed.
361
			if (substr($response, 0, 4) != '227 ')
362
			{
363
				$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...
364
				return false;
365
			}
366
367
			// Snatch the IP and port information, or die horribly trying...
368
			if (preg_match('~\((\d+),\s*(\d+),\s*(\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+))\)~', $response, $match) == 0)
369
			{
370
				$this->error = 'bad_response';
371
				return false;
372
			}
373
374
			// This is pretty simple - store it for later use ;).
375
			$this->pasv = array('ip' => $match[1] . '.' . $match[2] . '.' . $match[3] . '.' . $match[4], 'port' => $match[5] * 256 + $match[6]);
376
377
			return true;
378
		}
379
380
		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...
381
		{
382
			// First, we have to be connected... very important.
383
			if (!is_resource($this->connection))
384
				return false;
385
386
			// I'd like one passive mode, please!
387
			if (!$this->passive())
388
				return false;
389
390
			// Seems logical enough, so far...
391
			fwrite($this->connection, 'STOR ' . $ftp_file . "\r\n");
392
393
			// Okay, now we connect to the data port.  If it doesn't work out, it's probably "file already exists", etc.
394
			$fp = @fsockopen($this->pasv['ip'], $this->pasv['port'], $err, $err, 5);
395
			if (!$fp || !$this->check_response(150))
396
			{
397
				$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...
398
				@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...
399
				return false;
400
			}
401
402
			// This may look strange, but we're just closing it to indicate a zero-byte upload.
403
			fclose($fp);
404
			if (!$this->check_response(226))
405
			{
406
				$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...
407
				return false;
408
			}
409
410
			return true;
411
		}
412
413 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...
414
		{
415
			// Are we even connected...?
416
			if (!is_resource($this->connection))
417
				return false;
418
419
			// Passive... non-agressive...
420
			if (!$this->passive())
421
				return false;
422
423
			// Get the listing!
424
			fwrite($this->connection, 'LIST -1' . ($search ? 'R' : '') . ($ftp_path == '' ? '' : ' ' . $ftp_path) . "\r\n");
425
426
			// Connect, assuming we've got a connection.
427
			$fp = @fsockopen($this->pasv['ip'], $this->pasv['port'], $err, $err, 5);
428
			if (!$fp || !$this->check_response(array(150, 125)))
429
			{
430
				$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...
431
				@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...
432
				return false;
433
			}
434
435
			// Read in the file listing.
436
			$data = '';
437
			while (!feof($fp))
438
				$data .= fread($fp, 4096);
439
			fclose($fp);
440
441
			// Everything go okay?
442
			if (!$this->check_response(226))
443
			{
444
				$this->error = 'bad_response';
445
				return false;
446
			}
447
448
			return $data;
449
		}
450
451 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...
452
		{
453
			if ($listing === null)
454
				$listing = $this->list_dir('', true);
455
			$listing = explode("\n", $listing);
456
457
			@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...
458
			$time = time();
459
			do
460
				$response = fgets($this->connection, 1024);
461
			while (substr($response, 3, 1) != ' ' && time() - $time < 5);
462
463
			// Check for 257!
464
			if (preg_match('~^257 "(.+?)" ~', $response, $match) != 0)
465
				$current_dir = strtr($match[1], array('""' => '"'));
466
			else
467
				$current_dir = '';
468
469
			for ($i = 0, $n = count($listing); $i < $n; $i++)
470
			{
471
				if (trim($listing[$i]) == '' && isset($listing[$i + 1]))
472
				{
473
					$current_dir = substr(trim($listing[++$i]), 0, -1);
474
					$i++;
475
				}
476
477
				// Okay, this file's name is:
478
				$listing[$i] = $current_dir . '/' . trim(strlen($listing[$i]) > 30 ? strrchr($listing[$i], ' ') : $listing[$i]);
479
480
				if (substr($file, 0, 1) == '*' && substr($listing[$i], -(strlen($file) - 1)) == substr($file, 1))
481
					return $listing[$i];
482
				if (substr($file, -1) == '*' && substr($listing[$i], 0, strlen($file) - 1) == substr($file, 0, -1))
483
					return $listing[$i];
484
				if (basename($listing[$i]) == $file || $listing[$i] == $file)
485
					return $listing[$i];
486
			}
487
488
			return false;
489
		}
490
491
		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...
492
		{
493
			// We must be connected to the server to do something.
494
			if (!is_resource($this->connection))
495
				return false;
496
497
			// Make this new beautiful directory!
498
			fwrite($this->connection, 'MKD ' . $ftp_dir . "\r\n");
499
			if (!$this->check_response(257))
500
			{
501
				$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...
502
				return false;
503
			}
504
505
			return true;
506
		}
507
508 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...
509
		{
510
			$username = '';
511
512
			if (isset($_SERVER['DOCUMENT_ROOT']))
513
			{
514
				if (preg_match('~^/home[2]?/([^/]+?)/public_html~', $_SERVER['DOCUMENT_ROOT'], $match))
515
				{
516
					$username = $match[1];
517
518
					$path = strtr($_SERVER['DOCUMENT_ROOT'], array('/home/' . $match[1] . '/' => '', '/home2/' . $match[1] . '/' => ''));
519
520
					if (substr($path, -1) == '/')
521
						$path = substr($path, 0, -1);
522
523
					if (strlen(dirname($_SERVER['PHP_SELF'])) > 1)
524
						$path .= dirname($_SERVER['PHP_SELF']);
525
				}
526
				elseif (substr($filesystem_path, 0, 9) == '/var/www/')
527
					$path = substr($filesystem_path, 8);
528
				else
529
					$path = strtr(strtr($filesystem_path, array('\\' => '/')), array($_SERVER['DOCUMENT_ROOT'] => ''));
530
			}
531
			else
532
				$path = '';
533
534
			if (is_resource($this->connection) && $this->list_dir($path) == '')
535
			{
536
				$data = $this->list_dir('', true);
537
538
				if ($lookup_file === null)
539
					$lookup_file = $_SERVER['PHP_SELF'];
540
541
				$found_path = dirname($this->locate('*' . basename(dirname($lookup_file)) . '/' . basename($lookup_file), $data));
542
				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...
543
					$found_path = dirname($this->locate(basename($lookup_file)));
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 a non-empty string, consider using the more explicit !== '' instead.
Loading history...
545
					$path = $found_path;
546
			}
547
			elseif (is_resource($this->connection))
548
				$found_path = true;
549
550
			return array($username, $path, isset($found_path));
551
		}
552
553
		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...
554
		{
555
			// Goodbye!
556
			fwrite($this->connection, 'QUIT' . "\r\n");
557
			fclose($this->connection);
558
559
			return true;
560
		}
561
	}
562
}
563
564
// Don't do security check if on Yabbse
565
if (!isset($modSettings['smfVersion']))
566
	$disable_security = true;
567
568
// This only exists if we're on SMF ;)
569
if (isset($modSettings['smfVersion']))
570
{
571
	$request = $smcFunc['db_query']('', '
572
		SELECT variable, value
573
		FROM {db_prefix}themes
574
		WHERE id_theme = {int:id_theme}
575
			AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
576
		array(
577
			'id_theme' => 1,
578
			'theme_url' => 'theme_url',
579
			'theme_dir' => 'theme_dir',
580
			'images_url' => 'images_url',
581
			'db_error_skip' => true,
582
		)
583
	);
584 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...
585
		$modSettings[$row['variable']] = $row['value'];
586
	$smcFunc['db_free_result']($request);
587
}
588
589
if (!isset($modSettings['theme_url']))
590
{
591
	$modSettings['theme_dir'] = $boarddir . '/Themes/default';
592
	$modSettings['theme_url'] = 'Themes/default';
593
	$modSettings['images_url'] = 'Themes/default/images';
594
}
595
if (!isset($settings['default_theme_url']))
596
	$settings['default_theme_url'] = $modSettings['theme_url'];
597
if (!isset($settings['default_theme_dir']))
598
	$settings['default_theme_dir'] = $modSettings['theme_dir'];
599
600
$upcontext['is_large_forum'] = (empty($modSettings['smfVersion']) || $modSettings['smfVersion'] <= '1.1 RC1') && !empty($modSettings['totalMessages']) && $modSettings['totalMessages'] > 75000;
601
// Default title...
602
$upcontext['page_title'] = isset($modSettings['smfVersion']) ? 'Updating Your SMF Install!' : 'Upgrading from YaBB SE!';
603
604
// Have we got tracking data - if so use it (It will be clean!)
605
if (isset($_GET['data']))
606
{
607
	$upcontext['upgrade_status'] = safe_unserialize(base64_decode($_GET['data']));
608
	$upcontext['current_step'] = $upcontext['upgrade_status']['curstep'];
609
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
610
	$upcontext['rid'] = $upcontext['upgrade_status']['rid'];
611
	$is_debug = $upcontext['upgrade_status']['debug'];
612
	$support_js = $upcontext['upgrade_status']['js'];
613
614
	// Load the language.
615 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...
616
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
617
}
618
// Set the defaults.
619
else
620
{
621
	$upcontext['current_step'] = 0;
622
	$upcontext['rid'] = mt_rand(0, 5000);
623
	$upcontext['upgrade_status'] = array(
624
		'curstep' => 0,
625
		'lang' => isset($_GET['lang']) ? $_GET['lang'] : basename($language, '.lng'),
626
		'rid' => $upcontext['rid'],
627
		'pass' => 0,
628
		'debug' => 0,
629
		'js' => 0,
630
	);
631
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
632
}
633
634
// If this isn't the first stage see whether they are logging in and resuming.
635
if ($upcontext['current_step'] != 0 || !empty($upcontext['user']['step']))
636
	checkLogin();
637
638
if ($command_line)
639
	cmdStep0();
640
641
// Don't error if we're using xml.
642
if (isset($_GET['xml']))
643
	$upcontext['return_error'] = true;
644
645
// Loop through all the steps doing each one as required.
646
$upcontext['overall_percent'] = 0;
647
foreach ($upcontext['steps'] as $num => $step)
648
{
649
	if ($num >= $upcontext['current_step'])
650
	{
651
		// The current weight of this step in terms of overall progress.
652
		$upcontext['step_weight'] = $step[3];
653
		// Make sure we reset the skip button.
654
		$upcontext['skip'] = false;
655
656
		// We cannot proceed if we're not logged in.
657
		if ($num != 0 && !$disable_security && $upcontext['user']['pass'] != $upcontext['upgrade_status']['pass'])
658
		{
659
			$upcontext['steps'][0][2]();
660
			break;
661
		}
662
663
		// Call the step and if it returns false that means pause!
664 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...
665
			break;
666
		elseif (function_exists($step[2]))
667
			$upcontext['current_step']++;
668
	}
669
	$upcontext['overall_percent'] += $step[3];
670
}
671
672
upgradeExit();
673
674
// Exit the upgrade script.
675
function upgradeExit($fallThrough = false)
676
{
677
	global $upcontext, $upgradeurl, $boarddir, $command_line;
678
679
	// Save where we are...
680
	if (!empty($upcontext['current_step']) && !empty($upcontext['user']['id']))
681
	{
682
		$upcontext['user']['step'] = $upcontext['current_step'];
683
		$upcontext['user']['substep'] = $_GET['substep'];
684
		$upcontext['user']['updated'] = time();
685
		$upgradeData = base64_encode(safe_serialize($upcontext['user']));
686
		copy($boarddir . '/Settings.php', $boarddir . '/Settings_bak.php');
687
		changeSettings(array('upgradeData' => '"' . $upgradeData . '"'));
688
		updateLastError();
689
	}
690
691
	// Handle the progress of the step, if any.
692
	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
693
	{
694
		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
695
		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
696
	}
697
	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
698
699
	// We usually dump our templates out.
700
	if (!$fallThrough)
701
	{
702
		// This should not happen my dear... HELP ME DEVELOPERS!!
703
		if (!empty($command_line))
704
		{
705
			if (function_exists('debug_print_backtrace'))
706
				debug_print_backtrace();
707
708
			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.';
709
			flush();
710
			die();
711
		}
712
713
		if (!isset($_GET['xml']))
714
			template_upgrade_above();
715
		else
716
		{
717
			header('Content-Type: text/xml; charset=UTF-8');
718
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
719
			$upcontext['get_data'] = array();
720
			foreach ($_GET as $k => $v)
721
			{
722
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
723
				{
724
					$upcontext['get_data'][$k] = $v;
725
				}
726
			}
727
			template_xml_above();
728
		}
729
730
		// Call the template.
731
		if (isset($upcontext['sub_template']))
732
		{
733
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
734
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(safe_serialize($upcontext['upgrade_status']));
735
736
			// Custom stuff to pass back?
737
			if (!empty($upcontext['query_string']))
738
				$upcontext['form_url'] .= $upcontext['query_string'];
739
740
			call_user_func('template_' . $upcontext['sub_template']);
741
		}
742
743
		// Was there an error?
744
		if (!empty($upcontext['forced_error_message']))
745
			echo $upcontext['forced_error_message'];
746
747
		// Show the footer.
748
		if (!isset($_GET['xml']))
749
			template_upgrade_below();
750
		else
751
			template_xml_below();
752
	}
753
754
	// Bang - gone!
755
	die();
756
}
757
758
// Used to direct the user to another location.
759
function redirectLocation($location, $addForm = true)
760
{
761
	global $upgradeurl, $upcontext, $command_line;
762
763
	// Command line users can't be redirected.
764
	if ($command_line)
765
		upgradeExit(true);
766
767
	// Are we providing the core info?
768
	if ($addForm)
769
	{
770
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
771
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(safe_serialize($upcontext['upgrade_status'])) . $location;
772
	}
773
774
	while (@ob_end_clean());
775
	header('Location: ' . strtr($location, array('&amp;' => '&')));
776
777
	// Exit - saving status as we go.
778
	upgradeExit(true);
779
}
780
781
// Load all essential data and connect to the DB as this is pre SSI.php
782
function loadEssentialData()
783
{
784
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection, $db_prefix, $db_character_set, $db_type;
785
	global $modSettings, $sourcedir, $smcFunc;
786
787
	// Do the non-SSI stuff...
788
	if (function_exists('set_magic_quotes_runtime'))
789
		@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...
790
791
	error_reporting(E_ALL);
792
	define('SMF', 1);
793
794
	// Start the session.
795
	if (@ini_get('session.save_handler') == 'user')
796
		@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...
797
	@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...
798
799
	if (empty($smcFunc))
800
		$smcFunc = array();
801
802
	// We need this for authentication and some upgrade code
803
	require_once($sourcedir . '/Subs-Auth.php');
804
805
	$smcFunc['strtolower'] = 'smf_strtolower';
806
807
808
	// Initialize everything...
809
	initialize_inputs();
810
811
	// Get the database going!
812
	if (empty($db_type))
813
		$db_type = 'mysql';
814
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
815
	{
816
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
817
818
		// Make the connection...
819
		$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, array('non_fatal' => true));
820
821
		// Oh dear god!!
822
		if ($db_connection === null)
823
			die('Unable to connect to database - please check username and password are correct in Settings.php');
824
825
		if (($db_type == 'mysql' || $db_type == 'mysqli') && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
826
			$smcFunc['db_query']('', '
827
			SET NAMES ' . $db_character_set,
828
			array(
829
				'db_error_skip' => true,
830
			)
831
		);
832
833
		// Load the modSettings data...
834
		$request = $smcFunc['db_query']('', '
835
			SELECT variable, value
836
			FROM {db_prefix}settings',
837
			array(
838
				'db_error_skip' => true,
839
			)
840
		);
841
		$modSettings = array();
842 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...
843
			$modSettings[$row['variable']] = $row['value'];
844
		$smcFunc['db_free_result']($request);
845
	}
846
	else
847
	{
848
		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.');
849
	}
850
851
	require_once($sourcedir . '/Subs.php');
852
853
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
854
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
855
	{
856
		require_once($sourcedir . '/QueryString.php');
857
		cleanRequest();
858
	}
859
860
	if (!isset($_GET['substep']))
861
		$_GET['substep'] = 0;
862
}
863
864
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...
865
{
866
	global $start_time, $upcontext, $db_type;
867
868
	$start_time = time();
869
870
	umask(0);
871
872
	// Fun.  Low PHP version...
873
	if (!isset($_GET))
874
	{
875
		$GLOBALS['_GET']['step'] = 0;
876
		return;
877
	}
878
879
	ob_start();
880
881
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
882
	ignore_user_abort(true);
883
884
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
885
	if (isset($_GET['delete']))
886
	{
887
		@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...
888
889
		$type = ($db_type == 'mysqli' ? 'mysql' : $db_type);
890
891
		// And the extra little files ;).
892
		@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...
893
		@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...
894
		@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...
895
		@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...
896
897
		$dh = opendir(dirname(__FILE__));
898
		while ($file = readdir($dh))
899
		{
900
			if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
901
				@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...
902
		}
903
		closedir($dh);
904
905
		// Legacy files while we're at it. NOTE: We only touch files we KNOW shouldn't be there.
906
		// 1.1 Sources files not in 2.0+
907
		@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...
908
		// 1.1 Templates that don't exist any more (e.g. renamed)
909
		@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...
910
		@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...
911
		// 1.1 JS files were stored in the main theme folder, but in 2.0+ are in the scripts/ folder
912
		@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...
913
		@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...
914
		@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...
915
		@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...
916
		@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...
917
918
		// 2.0 Sources files not in 2.1+
919
		@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...
920
		@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...
921
922
		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');
923
		exit;
924
	}
925
926
	// Anybody home?
927 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...
928
	{
929
		$upcontext['remote_files_available'] = false;
930
		$test = @fsockopen('www.simplemachines.org', 80, $errno, $errstr, 1);
931
		if ($test)
932
			$upcontext['remote_files_available'] = true;
933
		@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...
934
	}
935
936
	// Something is causing this to happen, and it's annoying.  Stop it.
937
	$temp = 'upgrade_php?step';
938
	while (strlen($temp) > 4)
939
	{
940
		if (isset($_GET[$temp]))
941
			unset($_GET[$temp]);
942
		$temp = substr($temp, 1);
943
	}
944
945
	// Force a step, defaulting to 0.
946
	$_GET['step'] = (int) @$_GET['step'];
947
	$_GET['substep'] = (int) @$_GET['substep'];
948
}
949
950
// Step 0 - Let's welcome them in and ask them to login!
951
function WelcomeLogin()
952
{
953
	global $boarddir, $sourcedir, $modSettings, $cachedir, $upgradeurl, $upcontext;
954
	global $smcFunc, $db_type, $databases, $txt, $boardurl;
955
956
	$upcontext['sub_template'] = 'welcome_message';
957
958
	$type = ($db_type == 'mysqli' ? 'mysql' : $db_type);
959
960
	// Check for some key files - one template, one language, and a new and an old source file.
961
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
962
		&& @file_exists($sourcedir . '/QueryString.php')
963
		&& @file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')
964
		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $type . '.sql');
965
966
	// Need legacy scripts?
967 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...
968
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_2-0_' . $type . '.sql');
969 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...
970
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-1.sql');
971 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...
972
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
973
974
	// This needs to exist!
975
	if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
976
		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>]');
977
	else
978
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
979
980
	if (!$check)
981
		// 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.
982
		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.');
983
984
	// Do they meet the install requirements?
985
	if (!php_version_check())
986
		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.');
987
988
	if (!db_version_check())
989
		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.');
990
991
	// Do some checks to make sure they have proper privileges
992
	db_extend('packages');
993
994
	// CREATE
995
	$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');
996
997
	// ALTER
998
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
999
1000
	// DROP
1001
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
1002
1003
	// Sorry... we need CREATE, ALTER and DROP
1004 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...
1005
		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.');
1006
1007
	// Do a quick version spot check.
1008
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
1009
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
1010
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
1011
		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.');
1012
1013
	// What absolutely needs to be writable?
1014
	$writable_files = array(
1015
		$boarddir . '/Settings.php',
1016
		$boarddir . '/Settings_bak.php',
1017
	);
1018
1019
	// Do we need to add this setting?
1020
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
1021
1022
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] .'/custom_avatar';
1023
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl .'/custom_avatar';
1024
1025
	// This little fellow has to cooperate...
1026
	quickFileWritable($custom_av_dir);
1027
1028
	// Are we good now?
1029
	if(!is_writable($custom_av_dir))
1030
		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));
1031
	elseif ($need_settings_update)
1032
	{
1033
		if (!function_exists('cache_put_data'))
1034
			require_once($sourcedir . '/Load.php');
1035
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
1036
		updateSettings(array('custom_avatar_url' => $custom_av_url));
1037
	}
1038
1039
	require_once($sourcedir . '/Security.php');
1040
1041
	// Check the cache directory.
1042
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
1043
	if (!file_exists($cachedir_temp))
1044
		@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...
1045
	if (!file_exists($cachedir_temp))
1046
		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.');
1047
1048
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
1049
		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>.');
1050
	elseif (!isset($_GET['skiplang']))
1051
	{
1052
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
1053
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
1054
1055
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
1056
			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>]');
1057
	}
1058
1059
	if (!makeFilesWritable($writable_files))
1060
		return false;
1061
1062
	// Check agreement.txt. (it may not exist, in which case $boarddir must be writable.)
1063 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...
1064
		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.');
1065
1066
	// Upgrade the agreement.
1067
	elseif (isset($modSettings['agreement']))
1068
	{
1069
		$fp = fopen($boarddir . '/agreement.txt', 'w');
1070
		fwrite($fp, $modSettings['agreement']);
1071
		fclose($fp);
1072
	}
1073
1074
	// We're going to check that their board dir setting is right in case they've been moving stuff around.
1075
	if (strtr($boarddir, array('/' => '', '\\' => '')) != strtr(dirname(__FILE__), array('/' => '', '\\' => '')))
1076
		$upcontext['warning'] = '
1077
			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>
1078
			<ul>
1079
				<li>Board Directory: ' . $boarddir . '</li>
1080
				<li>Source Directory: ' . $boarddir . '</li>
1081
				<li>Cache Directory: ' . $cachedir_temp . '</li>
1082
			</ul>
1083
			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.';
1084
1085
	// Either we're logged in or we're going to present the login.
1086
	if (checkLogin())
1087
		return true;
1088
1089
	$upcontext += createToken('login');
1090
1091
	return false;
1092
}
1093
1094
// Step 0.5: Does the login work?
1095
function checkLogin()
1096
{
1097
	global $modSettings, $upcontext, $disable_security;
1098
	global $smcFunc, $db_type, $support_js;
1099
1100
	// Are we trying to login?
1101
	if (isset($_POST['contbutt']) && (!empty($_POST['user']) || $disable_security))
1102
	{
1103
		// If we've disabled security pick a suitable name!
1104
		if (empty($_POST['user']))
1105
			$_POST['user'] = 'Administrator';
1106
1107
		// Before 2.0 these column names were different!
1108
		$oldDB = false;
1109
		if (empty($db_type) || $db_type == 'mysql' || $db_type == 'mysqli')
1110
		{
1111
			$request = $smcFunc['db_query']('', '
1112
				SHOW COLUMNS
1113
				FROM {db_prefix}members
1114
				LIKE {string:member_name}',
1115
				array(
1116
					'member_name' => 'memberName',
1117
					'db_error_skip' => true,
1118
				)
1119
			);
1120
			if ($smcFunc['db_num_rows']($request) != 0)
1121
				$oldDB = true;
1122
			$smcFunc['db_free_result']($request);
1123
		}
1124
1125
		// Get what we believe to be their details.
1126
		if (!$disable_security)
1127
		{
1128
			if ($oldDB)
1129
				$request = $smcFunc['db_query']('', '
1130
					SELECT id_member, memberName AS member_name, passwd, id_group,
1131
					additionalGroups AS additional_groups, lngfile
1132
					FROM {db_prefix}members
1133
					WHERE memberName = {string:member_name}',
1134
					array(
1135
						'member_name' => $_POST['user'],
1136
						'db_error_skip' => true,
1137
					)
1138
				);
1139
			else
1140
				$request = $smcFunc['db_query']('', '
1141
					SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile
1142
					FROM {db_prefix}members
1143
					WHERE member_name = {string:member_name}',
1144
					array(
1145
						'member_name' => $_POST['user'],
1146
						'db_error_skip' => true,
1147
					)
1148
				);
1149
			if ($smcFunc['db_num_rows']($request) != 0)
1150
			{
1151
				list ($id_member, $name, $password, $id_group, $addGroups, $user_language) = $smcFunc['db_fetch_row']($request);
1152
1153
				$groups = explode(',', $addGroups);
1154
				$groups[] = $id_group;
1155
1156
				foreach ($groups as $k => $v)
1157
					$groups[$k] = (int) $v;
1158
1159
				$sha_passwd = sha1(strtolower($name) . un_htmlspecialchars($_REQUEST['passwrd']));
1160
			}
1161
			else
1162
				$upcontext['username_incorrect'] = true;
1163
			$smcFunc['db_free_result']($request);
1164
		}
1165
		$upcontext['username'] = $_POST['user'];
1166
1167
		// Track whether javascript works!
1168
		if (!empty($_POST['js_works']))
1169
		{
1170
			$upcontext['upgrade_status']['js'] = 1;
1171
			$support_js = 1;
1172
		}
1173
		else
1174
			$support_js = 0;
1175
1176
		// Note down the version we are coming from.
1177
		if (!empty($modSettings['smfVersion']) && empty($upcontext['user']['version']))
1178
			$upcontext['user']['version'] = $modSettings['smfVersion'];
1179
1180
		// Didn't get anywhere?
1181
		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...
1182
		{
1183
			// MD5?
1184
			$md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user']));
1185
			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...
1186
			{
1187
				$upcontext['password_failed'] = true;
1188
				// Disable the hashing this time.
1189
				$upcontext['disable_login_hashing'] = true;
1190
			}
1191
		}
1192
1193
		if ((empty($upcontext['password_failed']) && !empty($name)) || $disable_security)
1194
		{
1195
			// Set the password.
1196
			if (!$disable_security)
1197
			{
1198
				// Do we actually have permission?
1199
				if (!in_array(1, $groups))
1200
				{
1201
					$request = $smcFunc['db_query']('', '
1202
						SELECT permission
1203
						FROM {db_prefix}permissions
1204
						WHERE id_group IN ({array_int:groups})
1205
							AND permission = {string:admin_forum}',
1206
						array(
1207
							'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...
1208
							'admin_forum' => 'admin_forum',
1209
							'db_error_skip' => true,
1210
						)
1211
					);
1212
					if ($smcFunc['db_num_rows']($request) == 0)
1213
						return throw_error('You need to be an admin to perform an upgrade!');
1214
					$smcFunc['db_free_result']($request);
1215
				}
1216
1217
				$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...
1218
				$upcontext['user']['name'] = $name;
1219
			}
1220
			else
1221
			{
1222
				$upcontext['user']['id'] = 1;
1223
				$upcontext['user']['name'] = 'Administrator';
1224
			}
1225
			$upcontext['user']['pass'] = mt_rand(0,60000);
1226
			// This basically is used to match the GET variables to Settings.php.
1227
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
1228
1229
			// Set the language to that of the user?
1230
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
1231
			{
1232
				$user_language = basename($user_language, '.lng');
1233
				$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
1234
				preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
1235
1236
				if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
1237
					$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'] . '.';
1238
				elseif (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . basename($user_language, '.lng') . '.php'))
1239
					$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'] . '.';
1240
				else
1241
				{
1242
					// Set this as the new language.
1243
					$upcontext['language'] = $user_language;
1244
					$upcontext['upgrade_status']['lang'] = $upcontext['language'];
1245
1246
					// Include the file.
1247
					require_once($modSettings['theme_dir'] . '/languages/Install.' . $user_language . '.php');
1248
				}
1249
			}
1250
1251
			// If we're resuming set the step and substep to be correct.
1252
			if (isset($_POST['cont']))
1253
			{
1254
				$upcontext['current_step'] = $upcontext['user']['step'];
1255
				$_GET['substep'] = $upcontext['user']['substep'];
1256
			}
1257
1258
			return true;
1259
		}
1260
	}
1261
1262
	return false;
1263
}
1264
1265
// Step 1: Do the maintenance and backup.
1266
function UpgradeOptions()
1267
{
1268
	global $db_prefix, $command_line, $modSettings, $is_debug, $smcFunc, $packagesdir, $tasksdir;
1269
	global $boarddir, $boardurl, $sourcedir, $maintenance, $cachedir, $upcontext, $db_type, $db_server, $db_last_error;
1270
1271
	$upcontext['sub_template'] = 'upgrade_options';
1272
	$upcontext['page_title'] = 'Upgrade Options';
1273
1274
	db_extend('packages');
1275
	$upcontext['karma_installed'] = array('good' => false, 'bad' => false);
1276
	$member_columns = $smcFunc['db_list_columns']('{db_prefix}members');
1277
1278
	$upcontext['karma_installed']['good'] = in_array('karma_good', $member_columns);
1279
	$upcontext['karma_installed']['bad'] = in_array('karma_bad', $member_columns);
1280
1281
	unset($member_columns);
1282
1283
	// If we've not submitted then we're done.
1284
	if (empty($_POST['upcont']))
1285
		return false;
1286
1287
	require_once($sourcedir . '/Subs-Admin.php');
1288
	updateSettingsFile(array('image_proxy_secret' => '\'' . substr(sha1(mt_rand()), 0, 20) . '\''));
1289
1290
	// Firstly, if they're enabling SM stat collection just do it.
1291
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']))
1292
	{
1293
		// Attempt to register the site etc.
1294
		$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
1295
		if ($fp)
1296
		{
1297
			$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
1298
			$out .= 'Host: www.simplemachines.org' . "\r\n";
1299
			$out .= 'Connection: Close' . "\r\n\r\n";
1300
			fwrite($fp, $out);
1301
1302
			$return_data = '';
1303
			while (!feof($fp))
1304
				$return_data .= fgets($fp, 128);
1305
1306
			fclose($fp);
1307
1308
			// Get the unique site ID.
1309
			preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);
1310
1311 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...
1312
				$smcFunc['db_insert']('replace',
1313
					$db_prefix . 'settings',
1314
					array('variable' => 'string', 'value' => 'string'),
1315
					array('allow_sm_stats', $ID[1]),
1316
					array('variable')
1317
				);
1318
		}
1319
	}
1320
	else
1321
		$smcFunc['db_query']('', '
1322
			DELETE FROM {db_prefix}settings
1323
			WHERE variable = {string:allow_sm_stats}',
1324
			array(
1325
				'allow_sm_stats' => 'allow_sm_stats',
1326
				'db_error_skip' => true,
1327
			)
1328
		);
1329
1330
	// Deleting old karma stuff?
1331
	if (!empty($_POST['delete_karma']))
1332
	{
1333
		// Delete old settings vars.
1334
		$smcFunc['db_query']('', '
1335
			DELETE FROM {db_prefix}settings
1336
			WHERE variable IN ({array_string:karma_vars})',
1337
			array(
1338
				'karma_vars' => array('karmaMode', 'karmaTimeRestrictAdmins', 'karmaWaitTime', 'karmaMinPosts', 'karmaLabel', 'karmaSmiteLabel', 'karmaApplaudLabel'),
1339
			)
1340
		);
1341
1342
		// Cleaning up old karma member settings.
1343
		if ($upcontext['karma_installed']['good'])
1344
			$smcFunc['db_query']('', '
1345
				ALTER TABLE {db_prefix}members
1346
				DROP karma_good',
1347
				array()
1348
			);
1349
1350
		// Does karma bad was enable?
1351
		if ($upcontext['karma_installed']['bad'])
1352
			$smcFunc['db_query']('', '
1353
				ALTER TABLE {db_prefix}members
1354
				DROP karma_bad',
1355
				array()
1356
			);
1357
1358
		// Cleaning up old karma permissions.
1359
		$smcFunc['db_query']('', '
1360
			DELETE FROM {db_prefix}permissions
1361
			WHERE permission = {string:karma_vars}',
1362
			array(
1363
				'karma_vars' => 'karma_edit',
1364
			)
1365
		);
1366
	}
1367
1368
	// Emptying the error log?
1369
	if (!empty($_POST['empty_error']))
1370
		$smcFunc['db_query']('truncate_table', '
1371
			TRUNCATE {db_prefix}log_errors',
1372
			array(
1373
			)
1374
		);
1375
1376
	$changes = array();
1377
1378
	// If we're overriding the language follow it through.
1379 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...
1380
		$changes['language'] = '\'' . $_GET['lang'] . '\'';
1381
1382
	if (!empty($_POST['maint']))
1383
	{
1384
		$changes['maintenance'] = '2';
1385
		// Remember what it was...
1386
		$upcontext['user']['main'] = $maintenance;
1387
1388
		if (!empty($_POST['maintitle']))
1389
		{
1390
			$changes['mtitle'] = '\'' . addslashes($_POST['maintitle']) . '\'';
1391
			$changes['mmessage'] = '\'' . addslashes($_POST['mainmessage']) . '\'';
1392
		}
1393
		else
1394
		{
1395
			$changes['mtitle'] = '\'Upgrading the forum...\'';
1396
			$changes['mmessage'] = '\'Don\\\'t worry, we will be back shortly with an updated forum.  It will only be a minute ;).\'';
1397
		}
1398
	}
1399
1400
	if ($command_line)
1401
		echo ' * Updating Settings.php...';
1402
1403
	// Backup the current one first.
1404
	copy($boarddir . '/Settings.php', $boarddir . '/Settings_bak.php');
1405
1406
	// Fix some old paths.
1407 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...
1408
		$changes['boarddir'] = '\'' . fixRelativePath($boarddir) . '\'';
1409
1410 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...
1411
		$changes['sourcedir'] = '\'' . fixRelativePath($sourcedir) . '\'';
1412
1413
	if (empty($cachedir) || substr($cachedir, 0, 1) == '.')
1414
		$changes['cachedir'] = '\'' . fixRelativePath($boarddir) . '/cache\'';
1415
1416
	// Not had the database type added before?
1417
	if (empty($db_type))
1418
		$changes['db_type'] = 'mysql';
1419
1420
	// For now we offer a option, this may change in future versions when mysql is completely removed.
1421
	if (!empty($_POST['convertMysql']) && $db_type == 'mysql')
1422
		$changes['db_type'] = '\'mysqli\'';
1423
1424
	// If they have a "host:port" setup for the host, split that into separate values
1425
	// You should never have a : in the hostname if you're not on MySQL, but better safe than sorry
1426
	if (strpos($db_server, ':') !== false && ($db_type == 'mysql' || $db_type == 'mysqli'))
1427
	{
1428
		list($db_server, $db_port) = explode(':', $db_server);
1429
1430
		$changes['db_server'] = '\'' . $db_server . '\'';
1431
1432
		// Only set this if we're not using the default port
1433
		if ($db_port != ini_get('mysql' . ($db_type == 'mysqli' || !empty($_POST['convertMysql']) ? 'i' : '') . '.default_port'))
1434
			$changes['db_port'] = (int) $db_port;
1435
	}
1436
	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...
1437
	{
1438
		// If db_port is set and is the same as the default, set it to ''
1439
		if ($db_type == 'mysql' || $db_type == 'mysqli')
1440
		{
1441
			if ($db_port == ini_get('mysql' . ($db_type == 'mysqli' || !empty($_POST['convertMysql']) ? 'i' : '') . '.default_port'))
1442
				$changes['db_port'] = '\'\'';
1443
			elseif ($db_type == 'postgresql' && $db_port == 5432)
1444
				$changes['db_port'] = '\'\'';
1445
		}
1446
	}
1447
1448
	// Maybe we haven't had this option yet?
1449
	if (empty($packagesdir))
1450
		$changes['packagesdir'] = '\'' . fixRelativePath($boarddir) . '/Packages\'';
1451
1452
	// Add support for $tasksdir var.
1453
	if (empty($tasksdir))
1454
		$changes['tasksdir'] = '\'' . fixRelativePath($sourcedir) . '/tasks\'';
1455
1456
	// @todo Maybe change the cookie name if going to 1.1, too?
1457
1458
	// Update Settings.php with the new settings.
1459
	changeSettings($changes);
1460
1461
	// Back up again before we do anything else, just in case...
1462
	copy($boarddir . '/Settings.php', $boarddir . '/Settings_bak.php');
1463
1464
	// Read the contents of the file in as a string
1465
	$settings_file = file_get_contents($boarddir . '/Settings.php');
1466
1467
	// Look to see if the new error-catching section is there...
1468
	if (stripos($settings_file, 'if (file_exists(dirname(__FILE__) . \'/db_last_error.php\'))') === false)
1469
	{
1470
		// This is what we want to add...
1471
		$error_catching_header = '
1472
########## Error-Catching ##########
1473
# Note: You shouldn\'t touch these settings.';
1474
1475
		$error_catching = '
1476
if (file_exists(dirname(__FILE__) . \'/db_last_error.php\'))
1477
	include(dirname(__FILE__) . \'/db_last_error.php\');
1478
1479
if (!isset($db_last_error))
1480
{
1481
	// File does not exist so lets try to create it
1482
	file_put_contents(dirname(__FILE__) . \'/db_last_error.php\', \'<\' . \'?\' . "php\n" . \'$db_last_error = 0;\' . "\n" . \'?\' . \'>\');
1483
	$db_last_error = 0;
1484
}
1485
';
1486
		// Before we go any further, check to see if the original code is there first...
1487
		if (stripos('########## Error-Catching ##########', $settings_file !== false))
1488
		{
1489
			$found_old = true;
1490
			// Replace the old line with the new code - assuming the header is already there
1491
			$settings_file = str_replace('$db_last_error = ' . $db_last_error . ';', $error_catching, $settings_file);
1492
		}
1493
		// What about just the db_last_error line?
1494
		elseif (stripos('$db_last_error =', $settings_file !== false))
1495
		{
1496
			$found_old = true;
1497
			// Replace the old line with the new code
1498
			$settings_file = str_replace('$db_last_error = ' . $db_last_error . ';', $error_catching_header . $error_catching, $settings_file);
1499
		}
1500
		else
1501
		{
1502
			$found_old = false;
1503
			// We want the comments as well as the code...
1504
			$error_catching = $error_catching_header . $error_catching;
1505
		}
1506
1507
		// Blank out the file - done to fix a oddity with some servers.
1508
		$fp = fopen($boarddir . '/Settings.php', 'w');
1509
		fclose($fp);
1510
1511
		// Open the file for writing
1512
		$file = fopen($boarddir . '/Settings.php', 'r+');
1513
1514
		// Write the original contents...
1515
		fwrite($file, $settings_file);
1516
1517
		// If we didn't find the old code, add the new code at the end instead...
1518
		if (!$found_old)
1519
		{
1520
			// Go to the position two bytes before the end - in front of the closing PHP tag
1521
			fseek($file, -2, SEEK_END);
1522
1523
			// Append our new data - the extra line break above will prevent us from breaking things...
1524
			fwrite($file, $error_catching);
1525
		}
1526
1527
		// Close the file
1528
		fclose($file);
1529
	}
1530
1531
	if ($command_line)
1532
		echo ' Successful.' . "\n";
1533
1534
	// Are we doing debug?
1535
	if (isset($_POST['debug']))
1536
	{
1537
		$upcontext['upgrade_status']['debug'] = true;
1538
		$is_debug = true;
1539
	}
1540
1541
	// If we're not backing up then jump one.
1542
	if (empty($_POST['backup']))
1543
		$upcontext['current_step']++;
1544
1545
	// If we've got here then let's proceed to the next step!
1546
	return true;
1547
}
1548
1549
// Backup the database - why not...
1550
function BackupDatabase()
1551
{
1552
	global $upcontext, $db_prefix, $command_line, $is_debug, $support_js, $file_steps, $smcFunc;
1553
1554
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1555
	$upcontext['page_title'] = 'Backup Database';
1556
1557
	// Done it already - js wise?
1558
	if (!empty($_POST['backup_done']))
1559
		return true;
1560
1561
	// Some useful stuff here.
1562
	db_extend();
1563
1564
	// Might need this as well
1565
	db_extend('packages');
1566
1567
	// Get all the table names.
1568
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1569
	$db = preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? strtr($match[1], array('`' => '')) : false;
1570
	$tables = $smcFunc['db_list_tables']($db, $filter);
1571
1572
	$table_names = array();
1573
	foreach ($tables as $table)
1574
		if (substr($table, 0, 7) !== 'backup_')
1575
			$table_names[] = $table;
1576
1577
	$upcontext['table_count'] = count($table_names);
1578
	$upcontext['cur_table_num'] = $_GET['substep'];
1579
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1580
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1581
	// For non-java auto submit...
1582
	$file_steps = $upcontext['table_count'];
1583
1584
	// What ones have we already done?
1585 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...
1586
		if ($id < $_GET['substep'])
1587
			$upcontext['previous_tables'][] = $table;
1588
1589
	if ($command_line)
1590
		echo 'Backing Up Tables.';
1591
1592
	// If we don't support javascript we backup here.
1593
	if (!$support_js || isset($_GET['xml']))
1594
	{
1595
		// Backup each table!
1596
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1597
		{
1598
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1599
			$upcontext['cur_table_num'] = $substep + 1;
1600
1601
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1602
1603
			// Do we need to pause?
1604
			nextSubstep($substep);
1605
1606
			backupTable($table_names[$substep]);
1607
1608
			// If this is XML to keep it nice for the user do one table at a time anyway!
1609
			if (isset($_GET['xml']))
1610
				return upgradeExit();
1611
		}
1612
1613
		if ($is_debug && $command_line)
1614
		{
1615
			echo "\n" . ' Successful.\'' . "\n";
1616
			flush();
1617
		}
1618
		$upcontext['step_progress'] = 100;
1619
1620
		$_GET['substep'] = 0;
1621
		// Make sure we move on!
1622
		return true;
1623
	}
1624
1625
	// Either way next place to post will be database changes!
1626
	$_GET['substep'] = 0;
1627
	return false;
1628
}
1629
1630
// Backup one table...
1631
function backupTable($table)
1632
{
1633
	global $is_debug, $command_line, $db_prefix, $smcFunc;
1634
1635
	if ($command_line)
1636
	{
1637
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1638
		flush();
1639
	}
1640
1641
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1642
1643
	if ($command_line)
1644
		echo ' done.';
1645
}
1646
1647
// Step 2: Everything.
1648
function DatabaseChanges()
1649
{
1650
	global $db_prefix, $modSettings, $command_line, $smcFunc;
1651
	global $upcontext, $support_js, $db_type;
1652
1653
	// Have we just completed this?
1654
	if (!empty($_POST['database_done']))
1655
		return true;
1656
1657
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1658
	$upcontext['page_title'] = 'Database Changes';
1659
1660
	$type = ($db_type == 'mysqli' ? 'mysql' : $db_type);
1661
1662
	// All possible files.
1663
	// Name, <version, insert_on_complete
1664
	$files = array(
1665
		array('upgrade_1-0.sql', '1.1', '1.1 RC0'),
1666
		array('upgrade_1-1.sql', '2.0', '2.0 a'),
1667
		array('upgrade_2-0_' . $type . '.sql', '2.1', '2.1 dev0'),
1668
		array('upgrade_2-1_' . $type . '.sql', '3.0', SMF_VERSION),
1669
	);
1670
1671
	// How many files are there in total?
1672
	if (isset($_GET['filecount']))
1673
		$upcontext['file_count'] = (int) $_GET['filecount'];
1674
	else
1675
	{
1676
		$upcontext['file_count'] = 0;
1677
		foreach ($files as $file)
1678
		{
1679
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1680
				$upcontext['file_count']++;
1681
		}
1682
	}
1683
1684
	// Do each file!
1685
	$did_not_do = count($files) - $upcontext['file_count'];
1686
	$upcontext['step_progress'] = 0;
1687
	$upcontext['cur_file_num'] = 0;
1688
	foreach ($files as $file)
1689
	{
1690
		if ($did_not_do)
1691
			$did_not_do--;
1692
		else
1693
		{
1694
			$upcontext['cur_file_num']++;
1695
			$upcontext['cur_file_name'] = $file[0];
1696
			// Do we actually need to do this still?
1697
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1698
			{
1699
				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1700 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...
1701
				{
1702
					// Only update the version of this if complete.
1703
					$smcFunc['db_insert']('replace',
1704
						$db_prefix . 'settings',
1705
						array('variable' => 'string', 'value' => 'string'),
1706
						array('smfVersion', $file[2]),
1707
						array('variable')
1708
					);
1709
1710
					$modSettings['smfVersion'] = $file[2];
1711
				}
1712
1713
				// If this is XML we only do this stuff once.
1714
				if (isset($_GET['xml']))
1715
				{
1716
					// Flag to move on to the next.
1717
					$upcontext['completed_step'] = true;
1718
					// Did we complete the whole file?
1719
					if ($nextFile)
1720
						$upcontext['current_debug_item_num'] = -1;
1721
					return upgradeExit();
1722
				}
1723
				elseif ($support_js)
1724
					break;
1725
			}
1726
			// Set the progress bar to be right as if we had - even if we hadn't...
1727
			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1728
		}
1729
	}
1730
1731
	$_GET['substep'] = 0;
1732
	// So the template knows we're done.
1733
	if (!$support_js)
1734
	{
1735
		$upcontext['changes_complete'] = true;
1736
1737
		return true;
1738
	}
1739
	return false;
1740
}
1741
1742
// Clean up any mods installed...
1743
function CleanupMods()
1744
{
1745
	global $db_prefix, $upcontext, $boarddir, $packagesdir, $settings, $smcFunc, $command_line;
1746
1747
	// Sorry. Not supported for command line users.
1748
	if ($command_line)
1749
		return true;
1750
1751
	// Skipping first?
1752
	if (!empty($_POST['skip']))
1753
	{
1754
		unset($_POST['skip']);
1755
		return true;
1756
	}
1757
1758
	// If we get here withOUT SSI we need to redirect to ensure we get it!
1759
	if (!isset($_GET['ssi']) || !function_exists('mktree'))
1760
		redirectLocation('&ssi=1');
1761
1762
	$upcontext['sub_template'] = 'clean_mods';
1763
	$upcontext['page_title'] = 'Cleanup Modifications';
1764
1765
	// This can be skipped.
1766
	$upcontext['skip'] = true;
1767
1768
	// If we're on the second redirect continue...
1769
	if (isset($_POST['cleandone2']))
1770
		return true;
1771
1772
	// Do we already know about some writable files?
1773
	if (isset($_POST['writable_files']))
1774
	{
1775
		$writable_files = safe_unserialize(base64_decode($_POST['writable_files']));
1776
		if (!makeFilesWritable($writable_files))
1777
		{
1778
			// What have we left?
1779
			$upcontext['writable_files'] = $writable_files;
1780
			return false;
1781
		}
1782
	}
1783
1784
	// Make sure we have some sort of packages directory.
1785
	if (!isset($packagesdir))
1786
		$packagesdir = $boarddir . '/Packages';
1787
1788
	// Load all theme paths....
1789
	$request = $smcFunc['db_query']('', '
1790
		SELECT id_theme, variable, value
1791
		FROM {db_prefix}themes
1792
		WHERE id_member = {int:id_member}
1793
			AND variable IN ({string:theme_dir}, {string:images_url})',
1794
		array(
1795
			'id_member' => 0,
1796
			'theme_dir' => 'theme_dir',
1797
			'images_url' => 'images_url',
1798
			'db_error_skip' => true,
1799
		)
1800
	);
1801
	$theme_paths = array();
1802
	while ($row = $smcFunc['db_fetch_assoc']($request))
1803
	{
1804
		if ($row['id_theme'] == 1)
1805
			$settings['default_' . $row['variable']] = $row['value'];
1806
		elseif ($row['variable'] == 'theme_dir')
1807
			$theme_paths[$row['id_theme']][$row['variable']] = $row['value'];
1808
	}
1809
	$smcFunc['db_free_result']($request);
1810
1811
	// Are there are mods installed that may need uninstalling?
1812
	$request = $smcFunc['db_query']('', '
1813
		SELECT id_install, filename, name, themes_installed, version
1814
		FROM {db_prefix}log_packages
1815
		WHERE install_state = {int:installed}
1816
		ORDER BY time_installed DESC',
1817
		array(
1818
			'installed' => 1,
1819
			'db_error_skip' => true,
1820
		)
1821
	);
1822
	$upcontext['packages'] = array();
1823
	while ($row = $smcFunc['db_fetch_assoc']($request))
1824
	{
1825
		// Work out the status.
1826
		if (!file_exists($packagesdir . '/' . $row['filename']))
1827
		{
1828
			$status = 'Missing';
1829
			$status_color = 'red';
1830
			$result = 'Removed';
1831
		}
1832
		else
1833
		{
1834
			$status = 'Installed';
1835
			$status_color = 'green';
1836
			$result = 'No Action Needed';
1837
		}
1838
1839
		$upcontext['packages'][$row['id_install']] = array(
1840
			'id' => $row['id_install'],
1841
			'themes' => explode(',', $row['themes_installed']),
1842
			'name' => $row['name'],
1843
			'filename' => $row['filename'],
1844
			'missing_file' => file_exists($packagesdir . '/' . $row['filename']) ? 0 : 1,
1845
			'files' => array(),
1846
			'file_count' => 0,
1847
			'status' => $status,
1848
			'result' => $result,
1849
			'color' => $status_color,
1850
			'version' => $row['version'],
1851
			'needs_removing' => false,
1852
		);
1853
	}
1854
	$smcFunc['db_free_result']($request);
1855
1856
	// Don't carry on if there are none.
1857
	if (empty($upcontext['packages']))
1858
		return true;
1859
1860
	// Setup some basics.
1861
	if (!empty($upcontext['user']['version']))
1862
		$_SESSION['version_emulate'] = $upcontext['user']['version'];
1863
1864
	// Before we get started, don't report notice errors.
1865
	$oldErrorReporting = error_reporting(E_ALL ^ E_NOTICE);
1866
1867
	if (!mktree($packagesdir . '/temp', 0755))
1868
	{
1869
		deltree($packagesdir . '/temp', false);
1870
		if (!mktree($packagesdir . '/temp', 0777))
1871
		{
1872
			deltree($packagesdir . '/temp', false);
1873
			// @todo Error here - plus chmod!
1874
		}
1875
	}
1876
1877
	// Anything which reinstalled should not have its entry removed.
1878
	$reinstall_worked = array();
1879
1880
	// We're gonna be doing some removin'
1881
	$test = isset($_POST['cleandone']) ? false : true;
1882
	foreach ($upcontext['packages'] as $id => $package)
1883
	{
1884
		// Can't do anything about this....
1885
		if ($package['missing_file'])
1886
			continue;
1887
1888
		// Not testing *and* this wasn't checked?
1889
		if (!$test && (!isset($_POST['remove']) || !isset($_POST['remove'][$id])))
1890
			continue;
1891
1892
		// What are the themes this was installed into?
1893
		$cur_theme_paths = array();
1894
		foreach ($theme_paths as $tid => $data)
1895
			if ($tid != 1 && in_array($tid, $package['themes']))
1896
				$cur_theme_paths[$tid] = $data;
1897
1898
		// Get the modifications data if applicable.
1899
		$filename = $package['filename'];
1900
		$packageInfo = getPackageInfo($filename);
1901
		if (!is_array($packageInfo))
1902
			continue;
1903
1904
		$info = parsePackageInfo($packageInfo['xml'], $test, 'uninstall');
1905
		// Also get the reinstall details...
1906
		if (isset($_POST['remove']))
1907
			$infoInstall = parsePackageInfo($packageInfo['xml'], true);
1908
1909 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...
1910
			read_tgz_file($packagesdir . '/' . $filename, $packagesdir . '/temp');
1911
		else
1912
			copytree($packagesdir . '/' . $filename, $packagesdir . '/temp');
1913
1914
		// Work out how we uninstall...
1915
		$files = array();
1916
		foreach ($info as $change)
1917
		{
1918
			// Work out two things:
1919
			// 1) Whether it's installed at the moment - and if so whether its fully installed, and:
1920
			// 2) Whether it could be installed on the new version.
1921
			if ($change['type'] == 'modification')
1922
			{
1923
				$contents = @file_get_contents($packagesdir . '/temp/' . $upcontext['base_path'] . $change['filename']);
1924
				if ($change['boardmod'])
1925
					$results = parseBoardMod($contents, $test, $change['reverse'], $cur_theme_paths);
1926
				else
1927
					$results = parseModification($contents, $test, $change['reverse'], $cur_theme_paths);
1928
1929
				foreach ($results as $action)
1930
				{
1931
					// Something we can remove? Probably means it existed!
1932
					if (($action['type'] == 'replace' || $action['type'] == 'append' || (!empty($action['filename']) && $action['type'] == 'failure')) && !in_array($action['filename'], $files))
1933
						$files[] = $action['filename'];
1934
					if ($action['type'] == 'failure')
1935
					{
1936
						$upcontext['packages'][$id]['needs_removing'] = true;
1937
						$upcontext['packages'][$id]['status'] = 'Reinstall Required';
1938
						$upcontext['packages'][$id]['color'] = '#FD6435';
1939
					}
1940
				}
1941
			}
1942
		}
1943
1944
		// Store this info for the template as appropriate.
1945
		$upcontext['packages'][$id]['files'] = $files;
1946
		$upcontext['packages'][$id]['file_count'] = count($files);
1947
1948
		// If we've done something save the changes!
1949
		if (!$test)
1950
			package_flush_cache();
1951
1952
		// Are we attempting to reinstall this thing?
1953
		if (isset($_POST['remove']) && !$test && isset($infoInstall))
1954
		{
1955
			// Need to extract again I'm afraid.
1956 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...
1957
				read_tgz_file($packagesdir . '/' . $filename, $packagesdir . '/temp');
1958
			else
1959
				copytree($packagesdir . '/' . $filename, $packagesdir . '/temp');
1960
1961
			$errors = false;
1962
			$upcontext['packages'][$id]['result'] = 'Removed';
1963
			foreach ($infoInstall as $change)
1964
			{
1965
				if ($change['type'] == 'modification')
1966
				{
1967
					$contents = @file_get_contents($packagesdir . '/temp/' . $upcontext['base_path'] . $change['filename']);
1968 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...
1969
						$results = parseBoardMod($contents, true, $change['reverse'], $cur_theme_paths);
1970
					else
1971
						$results = parseModification($contents, true, $change['reverse'], $cur_theme_paths);
1972
1973
					// Are there any errors?
1974
					foreach ($results as $action)
1975
						if ($action['type'] == 'failure')
1976
							$errors = true;
1977
				}
1978
			}
1979
			if (!$errors)
1980
			{
1981
				$reinstall_worked[] = $id;
1982
				$upcontext['packages'][$id]['result'] = 'Reinstalled';
1983
				$upcontext['packages'][$id]['color'] = 'green';
1984
				foreach ($infoInstall as $change)
1985
				{
1986
					if ($change['type'] == 'modification')
1987
					{
1988
						$contents = @file_get_contents($packagesdir . '/temp/' . $upcontext['base_path'] . $change['filename']);
1989 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...
1990
							$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...
1991
						else
1992
							$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...
1993
					}
1994
				}
1995
1996
				// Save the changes.
1997
				package_flush_cache();
1998
			}
1999
		}
2000
	}
2001
2002
	// Put errors back on a sec.
2003
	error_reporting($oldErrorReporting);
2004
2005
	// Check everything is writable.
2006
	if ($test && !empty($upcontext['packages']))
2007
	{
2008
		$writable_files = array();
2009
		foreach ($upcontext['packages'] as $package)
2010
		{
2011
			if (!empty($package['files']))
2012
				foreach ($package['files'] as $file)
2013
					$writable_files[] = $file;
2014
		}
2015
2016
		if (!empty($writable_files))
2017
		{
2018
			$writable_files = array_unique($writable_files);
2019
			$upcontext['writable_files'] = $writable_files;
2020
2021
			if (!makeFilesWritable($writable_files))
2022
				return false;
2023
		}
2024
	}
2025
2026
	if (file_exists($packagesdir . '/temp'))
2027
		deltree($packagesdir . '/temp');
2028
2029
	// Removing/Reinstalling any packages?
2030
	if (isset($_POST['remove']))
2031
	{
2032
		$deletes = array();
2033
		foreach ($_POST['remove'] as $id => $dummy)
2034
		{
2035
			if (!in_array((int) $id, $reinstall_worked))
2036
				$deletes[] = (int) $id;
2037
		}
2038
2039
		if (!empty($deletes))
2040
			upgrade_query( '
2041
				UPDATE ' . $db_prefix . 'log_packages
2042
				SET install_state = 0
2043
				WHERE id_install IN (' . implode(',', $deletes) . ')');
2044
2045
		// Ensure we don't lose our changes!
2046
		package_put_contents($packagesdir . '/installed.list', time());
2047
2048
		$upcontext['sub_template'] = 'cleanup_done';
2049
		return false;
2050
	}
2051
	else
2052
	{
2053
		$allgood = true;
2054
		// Is there actually anything that needs our attention?
2055
		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...
2056
			if ($package['color'] != 'green')
2057
				$allgood = false;
2058
2059
		if ($allgood)
2060
			return true;
2061
	}
2062
2063
	$_GET['substep'] = 0;
2064
	return isset($_POST['cleandone']) ? true : false;
2065
}
2066
2067
2068
// Delete the damn thing!
2069
function DeleteUpgrade()
2070
{
2071
	global $command_line, $language, $upcontext, $boarddir, $sourcedir, $forum_version, $user_info, $maintenance, $smcFunc, $db_type;
2072
2073
	// Now it's nice to have some of the basic SMF source files.
2074
	if (!isset($_GET['ssi']) && !$command_line)
2075
		redirectLocation('&ssi=1');
2076
2077
	$upcontext['sub_template'] = 'upgrade_complete';
2078
	$upcontext['page_title'] = 'Upgrade Complete';
2079
2080
	$endl = $command_line ? "\n" : '<br>' . "\n";
2081
2082
	$changes = array(
2083
		'language' => '\'' . (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language) . '\'',
2084
		'db_error_send' => '1',
2085
		'upgradeData' => '#remove#',
2086
	);
2087
2088
	// Are we in maintenance mode?
2089
	if (isset($upcontext['user']['main']))
2090
	{
2091
		if ($command_line)
2092
			echo ' * ';
2093
		$upcontext['removed_maintenance'] = true;
2094
		$changes['maintenance'] = $upcontext['user']['main'];
2095
	}
2096
	// Otherwise if somehow we are in 2 let's go to 1.
2097
	elseif (!empty($maintenance) && $maintenance == 2)
2098
		$changes['maintenance'] = 1;
2099
2100
	// Wipe this out...
2101
	$upcontext['user'] = array();
2102
2103
	// Make a backup of Settings.php first as otherwise earlier changes are lost.
2104
	copy($boarddir . '/Settings.php', $boarddir . '/Settings_bak.php');
2105
	changeSettings($changes);
2106
2107
	// Clean any old cache files away.
2108
	clean_cache();
2109
2110
	// Can we delete the file?
2111
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
2112
2113
	// Now is the perfect time to fetch the SM files.
2114
	if ($command_line)
2115
		cli_scheduled_fetchSMfiles();
2116
	else
2117
	{
2118
		require_once($sourcedir . '/ScheduledTasks.php');
2119
		$forum_version = SMF_VERSION;  // The variable is usually defined in index.php so lets just use the constant to do it for us.
2120
		scheduled_fetchSMfiles(); // Now go get those files!
2121
	}
2122
2123
	// Log what we've done.
2124
	if (empty($user_info['id']))
2125
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
2126
2127
	// Log the action manually, so CLI still works.
2128
	$smcFunc['db_insert']('',
2129
		'{db_prefix}log_actions',
2130
		array(
2131
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'string-16', 'action' => 'string',
2132
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
2133
		),
2134
		array(
2135
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
2136
			0, 0, 0, safe_serialize(array('version' => $forum_version, 'member' => $user_info['id'])),
2137
		),
2138
		array('id_action')
2139
	);
2140
	$user_info['id'] = 0;
2141
2142
	// Save the current database version.
2143
	$server_version = $smcFunc['db_server_info']();
2144 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...
2145
		updateSettings(array('db_mysql_group_by_fix' => '1'));
2146
2147
	if ($command_line)
2148
	{
2149
		echo $endl;
2150
		echo 'Upgrade Complete!', $endl;
2151
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
2152
		exit;
2153
	}
2154
2155
	// Make sure it says we're done.
2156
	$upcontext['overall_percent'] = 100;
2157
	if (isset($upcontext['step_progress']))
2158
		unset($upcontext['step_progress']);
2159
2160
	$_GET['substep'] = 0;
2161
	return false;
2162
}
2163
2164
// Just like the built in one, but setup for CLI to not use themes.
2165
function cli_scheduled_fetchSMfiles()
2166
{
2167
	global $sourcedir, $language, $forum_version, $modSettings, $smcFunc;
2168
2169
	if (empty($modSettings['time_format']))
2170
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
2171
2172
	// What files do we want to get
2173
	$request = $smcFunc['db_query']('', '
2174
		SELECT id_file, filename, path, parameters
2175
		FROM {db_prefix}admin_info_files',
2176
		array(
2177
		)
2178
	);
2179
2180
	$js_files = array();
2181 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...
2182
	{
2183
		$js_files[$row['id_file']] = array(
2184
			'filename' => $row['filename'],
2185
			'path' => $row['path'],
2186
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode($forum_version)),
2187
		);
2188
	}
2189
	$smcFunc['db_free_result']($request);
2190
2191
	// We're gonna need fetch_web_data() to pull this off.
2192
	require_once($sourcedir . '/Subs-Package.php');
2193
2194
	foreach ($js_files as $ID_FILE => $file)
2195
	{
2196
		// Create the url
2197
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'http://www.simplemachines.org' : '';
2198
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
2199
2200
		// Get the file
2201
		$file_data = fetch_web_data($url);
2202
2203
		// If we got an error - give up - the site might be down.
2204
		if ($file_data === false)
2205
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
2206
2207
		// Save the file to the database.
2208
		$smcFunc['db_query']('substring', '
2209
			UPDATE {db_prefix}admin_info_files
2210
			SET data = SUBSTRING({string:file_data}, 1, 65534)
2211
			WHERE id_file = {int:id_file}',
2212
			array(
2213
				'id_file' => $ID_FILE,
2214
				'file_data' => $file_data,
2215
			)
2216
		);
2217
	}
2218
	return true;
2219
}
2220
2221
function convertSettingsToTheme()
2222
{
2223
	global $db_prefix, $modSettings, $smcFunc;
2224
2225
	$values = array(
2226
		'show_latest_member' => @$GLOBALS['showlatestmember'],
2227
		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
2228
		'show_modify' => @$GLOBALS['showmodify'],
2229
		'show_user_images' => @$GLOBALS['showuserpic'],
2230
		'show_blurb' => @$GLOBALS['showusertext'],
2231
		'show_gender' => @$GLOBALS['showgenderimage'],
2232
		'show_newsfader' => @$GLOBALS['shownewsfader'],
2233
		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
2234
		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
2235
		'linktree_link' => @$GLOBALS['curposlinks'],
2236
		'show_profile_buttons' => @$GLOBALS['profilebutton'],
2237
		'show_mark_read' => @$GLOBALS['showmarkread'],
2238
		'newsfader_time' => @$GLOBALS['fadertime'],
2239
		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
2240
		'enable_news' => @$GLOBALS['enable_news'],
2241
		'return_to_post' => @$modSettings['returnToPost'],
2242
	);
2243
2244
	$themeData = array();
2245
	foreach ($values as $variable => $value)
2246
	{
2247
		if (!isset($value) || $value === null)
2248
			$value = 0;
2249
2250
		$themeData[] = array(0, 1, $variable, $value);
2251
	}
2252
	if (!empty($themeData))
2253
	{
2254
		$smcFunc['db_insert']('ignore',
2255
			$db_prefix . 'themes',
2256
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
2257
			$themeData,
2258
			array('id_member', 'id_theme', 'variable')
2259
		);
2260
	}
2261
}
2262
2263
// This function only works with MySQL but that's fine as it is only used for v1.0.
2264
function convertSettingstoOptions()
2265
{
2266
	global $modSettings, $smcFunc;
2267
2268
	// Format: new_setting -> old_setting_name.
2269
	$values = array(
2270
		'calendar_start_day' => 'cal_startmonday',
2271
		'view_newest_first' => 'viewNewestFirst',
2272
		'view_newest_pm_first' => 'viewNewestFirst',
2273
	);
2274
2275
	foreach ($values as $variable => $value)
2276
	{
2277
		if (empty($modSettings[$value[0]]))
2278
			continue;
2279
2280
		$smcFunc['db_query']('', '
2281
			INSERT IGNORE INTO {db_prefix}themes
2282
				(id_member, id_theme, variable, value)
2283
			SELECT id_member, 1, {string:variable}, {string:value}
2284
			FROM {db_prefix}members',
2285
			array(
2286
				'variable' => $variable,
2287
				'value' => $modSettings[$value[0]],
2288
				'db_error_skip' => true,
2289
			)
2290
		);
2291
2292
		$smcFunc['db_query']('', '
2293
			INSERT IGNORE INTO {db_prefix}themes
2294
				(id_member, id_theme, variable, value)
2295
			VALUES (-1, 1, {string:variable}, {string:value})',
2296
			array(
2297
				'variable' => $variable,
2298
				'value' => $modSettings[$value[0]],
2299
				'db_error_skip' => true,
2300
			)
2301
		);
2302
	}
2303
}
2304
2305
function changeSettings($config_vars)
2306
{
2307
	global $boarddir;
2308
2309
	$settingsArray = file($boarddir . '/Settings_bak.php');
2310
2311
	if (count($settingsArray) == 1)
2312
		$settingsArray = preg_split('~[\r\n]~', $settingsArray[0]);
2313
2314
	for ($i = 0, $n = count($settingsArray); $i < $n; $i++)
2315
	{
2316
		// Don't trim or bother with it if it's not a variable.
2317
		if (substr($settingsArray[$i], 0, 1) == '$')
2318
		{
2319
			$settingsArray[$i] = trim($settingsArray[$i]) . "\n";
2320
2321
			foreach ($config_vars as $var => $val)
2322
			{
2323
				if (isset($settingsArray[$i]) && strncasecmp($settingsArray[$i], '$' . $var, 1 + strlen($var)) == 0)
2324
				{
2325 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...
2326
						unset($settingsArray[$i]);
2327
					else
2328
					{
2329
						$comment = strstr(substr($settingsArray[$i], strpos($settingsArray[$i], ';')), '#');
2330
						$settingsArray[$i] = '$' . $var . ' = ' . $val . ';' . ($comment != '' ? "\t\t" . $comment : "\n");
2331
					}
2332
2333
					unset($config_vars[$var]);
2334
				}
2335
			}
2336
		}
2337 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...
2338
		{
2339
			if (trim(substr($settingsArray[$i], 0, 2)) == '?' . '>')
2340
				$end = $i;
2341
		}
2342
	}
2343
2344
	// Assume end-of-file if the end wasn't found.
2345
	if (empty($end) || $end < 10)
2346
		$end = count($settingsArray);
2347
2348 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...
2349
	{
2350
		$settingsArray[$end++] = '';
2351
		foreach ($config_vars as $var => $val)
2352
		{
2353
			if ($val != '#remove#')
2354
				$settingsArray[$end++] = '$' . $var . ' = ' . $val . ';' . "\n";
2355
		}
2356
	}
2357
	// This should be the last line and even last bytes of the file.
2358
	$settingsArray[$end] = '?' . '>';
2359
2360
	// Blank out the file - done to fix a oddity with some servers.
2361
	$fp = fopen($boarddir . '/Settings.php', 'w');
2362
	fclose($fp);
2363
2364
	$fp = fopen($boarddir . '/Settings.php', 'r+');
2365
	for ($i = 0; $i < $end; $i++)
2366
	{
2367
		if (isset($settingsArray[$i]))
2368
			fwrite($fp, strtr($settingsArray[$i], "\r", ''));
2369
	}
2370
	fwrite($fp, rtrim($settingsArray[$i]));
2371
	fclose($fp);
2372
}
2373
function updateLastError()
2374
{
2375
	// clear out the db_last_error file
2376
	file_put_contents(dirname(__FILE__) . '/db_last_error.php', '<' . '?' . "php\n" . '$db_last_error = 0;' . "\n" . '?' . '>');
2377
}
2378
2379
function php_version_check()
2380
{
2381
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
2382
}
2383
2384
function db_version_check()
2385
{
2386
	global $db_type, $databases;
2387
2388
	$curver = eval($databases[$db_type]['version_check']);
2389
	$curver = preg_replace('~\-.+?$~', '', $curver);
2390
2391
	return version_compare($databases[$db_type]['version'], $curver, '<=');
2392
}
2393
2394
function getMemberGroups()
2395
{
2396
	global $smcFunc;
2397
	static $member_groups = array();
2398
2399
	if (!empty($member_groups))
2400
		return $member_groups;
2401
2402
	$request = $smcFunc['db_query']('', '
2403
		SELECT group_name, id_group
2404
		FROM {db_prefix}membergroups
2405
		WHERE id_group = {int:admin_group} OR id_group > {int:old_group}',
2406
		array(
2407
			'admin_group' => 1,
2408
			'old_group' => 7,
2409
			'db_error_skip' => true,
2410
		)
2411
	);
2412
	if ($request === false)
2413
	{
2414
		$request = $smcFunc['db_query']('', '
2415
			SELECT membergroup, id_group
2416
			FROM {db_prefix}membergroups
2417
			WHERE id_group = {int:admin_group} OR id_group > {int:old_group}',
2418
			array(
2419
				'admin_group' => 1,
2420
				'old_group' => 7,
2421
				'db_error_skip' => true,
2422
			)
2423
		);
2424
	}
2425
	while ($row = $smcFunc['db_fetch_row']($request))
2426
		$member_groups[trim($row[0])] = $row[1];
2427
	$smcFunc['db_free_result']($request);
2428
2429
	return $member_groups;
2430
}
2431
2432
function fixRelativePath($path)
2433
{
2434
	global $install_path;
2435
2436
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
2437
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
2438
}
2439
2440
function parse_sql($filename)
2441
{
2442
	global $db_prefix, $db_collation, $boarddir, $boardurl, $command_line, $file_steps, $step_progress, $custom_warning;
2443
	global $upcontext, $support_js, $is_debug, $smcFunc, $databases, $db_type, $db_character_set;
2444
2445
/*
2446
	Failure allowed on:
2447
		- INSERT INTO but not INSERT IGNORE INTO.
2448
		- UPDATE IGNORE but not UPDATE.
2449
		- ALTER TABLE and ALTER IGNORE TABLE.
2450
		- DROP TABLE.
2451
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
2452
2453
	If a comment...
2454
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
2455
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
2456
		- is only ---#, it is "done." and then a break - only shown in debug.
2457
		- begins with ---{ it is a code block terminating at ---}.
2458
2459
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
2460
2461
	Replaces the following variables:
2462
		- {$boarddir}
2463
		- {$boardurl}
2464
		- {$db_prefix}
2465
		- {$db_collation}
2466
*/
2467
2468
	// May want to use extended functionality.
2469
	db_extend();
2470
	db_extend('packages');
2471
2472
	// Our custom error handler - does nothing but does stop public errors from XML!
2473
	if (!function_exists('sql_error_handler'))
2474
	{
2475
		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...
2476
		{
2477
			global $support_js;
2478
2479
			if ($support_js)
2480
				return true;
2481
			else
2482
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
2483
		}
2484
	}
2485
2486
	// Make our own error handler.
2487
	set_error_handler('sql_error_handler');
2488
2489
	// 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!
2490
	if (!empty($databases[$db_type]['utf8_support']) && version_compare($databases[$db_type]['utf8_version'], eval($databases[$db_type]['utf8_version_check']), '>'))
2491
	{
2492
		$request = $smcFunc['db_query']('', '
2493
			SHOW TABLE STATUS
2494
			LIKE {string:table_name}',
2495
			array(
2496
				'table_name' => "{$db_prefix}members",
2497
				'db_error_skip' => true,
2498
			)
2499
		);
2500
		if ($smcFunc['db_num_rows']($request) === 0)
2501
			die('Unable to find members table!');
2502
		$table_status = $smcFunc['db_fetch_assoc']($request);
2503
		$smcFunc['db_free_result']($request);
2504
2505
		if (!empty($table_status['Collation']))
2506
		{
2507
			$request = $smcFunc['db_query']('', '
2508
				SHOW COLLATION
2509
				LIKE {string:collation}',
2510
				array(
2511
					'collation' => $table_status['Collation'],
2512
					'db_error_skip' => true,
2513
				)
2514
			);
2515
			// Got something?
2516
			if ($smcFunc['db_num_rows']($request) !== 0)
2517
				$collation_info = $smcFunc['db_fetch_assoc']($request);
2518
			$smcFunc['db_free_result']($request);
2519
2520
			// Excellent!
2521
			if (!empty($collation_info['Collation']) && !empty($collation_info['Charset']))
2522
				$db_collation = ' CHARACTER SET ' . $collation_info['Charset'] . ' COLLATE ' . $collation_info['Collation'];
2523
		}
2524
	}
2525
	if (empty($db_collation))
2526
		$db_collation = '';
2527
2528
	$endl = $command_line ? "\n" : '<br>' . "\n";
2529
2530
	$lines = file($filename);
2531
2532
	$current_type = 'sql';
2533
	$current_data = '';
2534
	$substep = 0;
2535
	$last_step = '';
2536
2537
	// Make sure all newly created tables will have the proper characters set.
2538
	if (isset($db_character_set) && $db_character_set === 'utf8')
2539
		$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
2540
2541
	// Count the total number of steps within this file - for progress.
2542
	$file_steps = substr_count(implode('', $lines), '---#');
2543
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
2544
	$upcontext['debug_items'] = $file_steps;
2545
	$upcontext['current_item_num'] = 0;
2546
	$upcontext['current_item_name'] = '';
2547
	$upcontext['current_debug_item_num'] = 0;
2548
	$upcontext['current_debug_item_name'] = '';
2549
	// This array keeps a record of what we've done in case java is dead...
2550
	$upcontext['actioned_items'] = array();
2551
2552
	$done_something = false;
2553
2554
	foreach ($lines as $line_number => $line)
2555
	{
2556
		$do_current = $substep >= $_GET['substep'];
2557
2558
		// Get rid of any comments in the beginning of the line...
2559
		if (substr(trim($line), 0, 2) === '/*')
2560
			$line = preg_replace('~/\*.+?\*/~', '', $line);
2561
2562
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
2563
		if ($is_debug && !$support_js && $command_line)
2564
			flush();
2565
2566
		if (trim($line) === '')
2567
			continue;
2568
2569
		if (trim(substr($line, 0, 3)) === '---')
2570
		{
2571
			$type = substr($line, 3, 1);
2572
2573
			// An error??
2574
			if (trim($current_data) != '' && $type !== '}')
2575
			{
2576
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
2577
				if ($command_line)
2578
					echo $upcontext['error_message'];
2579
			}
2580
2581
			if ($type == ' ')
2582
			{
2583
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
2584
				{
2585
					echo ' Successful.', $endl;
2586
					flush();
2587
				}
2588
2589
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
2590
				$upcontext['current_item_num']++;
2591
				$upcontext['current_item_name'] = $last_step;
2592
2593
				if ($do_current)
2594
				{
2595
					$upcontext['actioned_items'][] = $last_step;
2596
					if ($command_line)
2597
						echo ' * ';
2598
				}
2599
			}
2600
			elseif ($type == '#')
2601
			{
2602
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
2603
2604
				$upcontext['current_debug_item_num']++;
2605
				if (trim($line) != '---#')
2606
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
2607
2608
				// Have we already done something?
2609
				if (isset($_GET['xml']) && $done_something)
2610
				{
2611
					restore_error_handler();
2612
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
2613
				}
2614
2615
				if ($do_current)
2616
				{
2617
					if (trim($line) == '---#' && $command_line)
2618
						echo ' done.', $endl;
2619
					elseif ($command_line)
2620
						echo ' +++ ', rtrim(substr($line, 4));
2621
					elseif (trim($line) != '---#')
2622
					{
2623
						if ($is_debug)
2624
							$upcontext['actioned_items'][] = htmlspecialchars(rtrim(substr($line, 4)));
2625
					}
2626
				}
2627
2628
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
2629
				{
2630
					if ($command_line)
2631
						echo ' * ';
2632
					else
2633
						$upcontext['actioned_items'][] = $last_step;
2634
				}
2635
2636
				// Small step - only if we're actually doing stuff.
2637
				if ($do_current)
2638
					nextSubstep(++$substep);
2639
				else
2640
					$substep++;
2641
			}
2642
			elseif ($type == '{')
2643
				$current_type = 'code';
2644
			elseif ($type == '}')
2645
			{
2646
				$current_type = 'sql';
2647
2648
				if (!$do_current)
2649
				{
2650
					$current_data = '';
2651
					continue;
2652
				}
2653
2654
				if (eval('global $db_prefix, $modSettings, $smcFunc; ' . $current_data) === false)
2655
				{
2656
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
2657
					if ($command_line)
2658
						echo $upcontext['error_message'];
2659
				}
2660
2661
				// Done with code!
2662
				$current_data = '';
2663
				$done_something = true;
2664
			}
2665
2666
			continue;
2667
		}
2668
2669
		$current_data .= $line;
2670
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
2671
		{
2672
			if ((!$support_js || isset($_GET['xml'])))
2673
			{
2674
				if (!$do_current)
2675
				{
2676
					$current_data = '';
2677
					continue;
2678
				}
2679
2680
				$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));
2681
2682
				upgrade_query($current_data);
2683
2684
				// @todo This will be how it kinda does it once mysql all stripped out - needed for postgre (etc).
2685
				/*
2686
				$result = $smcFunc['db_query']('', $current_data, false, false);
2687
				// Went wrong?
2688
				if (!$result)
2689
				{
2690
					// Bit of a bodge - do we want the error?
2691
					if (!empty($upcontext['return_error']))
2692
					{
2693
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
2694
						return false;
2695
					}
2696
				}*/
2697
				$done_something = true;
2698
			}
2699
			$current_data = '';
2700
		}
2701
		// If this is xml based and we're just getting the item name then that's grand.
2702
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
2703
		{
2704
			restore_error_handler();
2705
			return false;
2706
		}
2707
2708
		// Clean up by cleaning any step info.
2709
		$step_progress = array();
2710
		$custom_warning = '';
2711
	}
2712
2713
	// Put back the error handler.
2714
	restore_error_handler();
2715
2716
	if ($command_line)
2717
	{
2718
		echo ' Successful.' . "\n";
2719
		flush();
2720
	}
2721
2722
	$_GET['substep'] = 0;
2723
	return true;
2724
}
2725
2726
function upgrade_query($string, $unbuffered = false)
2727
{
2728
	global $db_connection, $db_server, $db_user, $db_passwd, $db_type, $command_line, $upcontext, $upgradeurl, $modSettings;
2729
	global $db_name, $db_unbuffered, $smcFunc;
2730
2731
	// Get the query result - working around some SMF specific security - just this once!
2732
	$modSettings['disableQueryCheck'] = true;
2733
	$db_unbuffered = $unbuffered;
2734
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
2735
	$db_unbuffered = false;
2736
2737
	// Failure?!
2738
	if ($result !== false)
2739
		return $result;
2740
2741
	$db_error_message = $smcFunc['db_error']($db_connection);
2742
	// If MySQL we do something more clever.
2743
	if ($db_type == 'mysql' || $db_type == 'mysqli')
2744
	{
2745
		$mysql_errno = ($db_type == 'mysqli') ? mysqli_errno($db_connection) : mysql_errno($db_connection);
2746
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR'));
2747
2748
		// Error numbers:
2749
		//    1016: Can't open file '....MYI'
2750
		//    1050: Table already exists.
2751
		//    1054: Unknown column name.
2752
		//    1060: Duplicate column name.
2753
		//    1061: Duplicate key name.
2754
		//    1062: Duplicate entry for unique key.
2755
		//    1068: Multiple primary keys.
2756
		//    1072: Key column '%s' doesn't exist in table.
2757
		//    1091: Can't drop key, doesn't exist.
2758
		//    1146: Table doesn't exist.
2759
		//    2013: Lost connection to server during query.
2760
2761
		if ($mysql_errno == 1016)
2762
		{
2763
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
2764
			{
2765
				if ($db_type == 'mysql')
2766
				{
2767
					mysql_query('REPAIR TABLE `' . $match[1] . '`');
2768
					$result = mysql_query($string);
2769
				}
2770
				else
2771
				{
2772
					mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
2773
					$result = mysqli_query($db_connection, $string);
2774
				}
2775
				if ($result !== false)
2776
					return $result;
2777
			}
2778
		}
2779
		elseif ($mysql_errno == 2013)
2780
		{
2781
			$db_connection = mysql_connect($db_server, $db_user, $db_passwd);
2782
			if ($db_type == 'mysql')
2783
			{
2784
				mysql_select_db($db_name, $db_connection);
2785
				if ($db_connection)
2786
				{
2787
					$result = mysql_query($string);
2788
					if ($result !== false)
2789
						return $result;
2790
				}
2791
			}
2792
			else
2793
			{
2794
				mysqli_select_db($db_connection, $db_name);
2795
				if ($db_connection)
2796
				{
2797
					$result = mysqli_query($db_connection, $string);
2798
					if ($result !== false)
2799
						return $result;
2800
				}
2801
			}
2802
		}
2803
		// Duplicate column name... should be okay ;).
2804 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...
2805
			return false;
2806
		// Duplicate insert... make sure it's the proper type of query ;).
2807 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...
2808
			return false;
2809
		// Creating an index on a non-existent column.
2810
		elseif ($mysql_errno == 1072)
2811
			return false;
2812
		elseif ($mysql_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
2813
			return false;
2814
	}
2815
	// If a table already exists don't go potty.
2816
	else
2817
	{
2818
		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U')))
2819
		{
2820
			if (strpos($db_error_message, 'exist') !== false)
2821
				return true;
2822
		}
2823
		elseif (strpos(trim($string), 'INSERT ') !== false)
2824
		{
2825
			if (strpos($db_error_message, 'duplicate') !== false)
2826
				return true;
2827
		}
2828
	}
2829
2830
	// Get the query string so we pass everything.
2831
	$query_string = '';
2832
	foreach ($_GET as $k => $v)
2833
		$query_string .= ';' . $k . '=' . $v;
2834
	if (strlen($query_string) != 0)
2835
		$query_string = '?' . substr($query_string, 1);
2836
2837
	if ($command_line)
2838
	{
2839
		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
2840
		die;
2841
	}
2842
2843
	// Bit of a bodge - do we want the error?
2844
	if (!empty($upcontext['return_error']))
2845
	{
2846
		$upcontext['error_message'] = $db_error_message;
2847
		return false;
2848
	}
2849
2850
	// Otherwise we have to display this somewhere appropriate if possible.
2851
	$upcontext['forced_error_message'] = '
2852
			<strong>Unsuccessful!</strong><br>
2853
2854
			<div style="margin: 2ex;">
2855
				This query:
2856
				<blockquote><pre>' . nl2br(htmlspecialchars(trim($string))) . ';</pre></blockquote>
2857
2858
				Caused the error:
2859
				<blockquote>' . nl2br(htmlspecialchars($db_error_message)) . '</blockquote>
2860
			</div>
2861
2862
			<form action="' . $upgradeurl . $query_string . '" method="post">
2863
				<input type="submit" value="Try again" class="button_submit">
2864
			</form>
2865
		</div>';
2866
2867
	upgradeExit();
2868
}
2869
2870
function smf_mysql_fetch_assoc($rs)
2871
{
2872
	global $db_type;
2873
	return ($db_type == 'mysql') ? mysql_fetch_assoc($rs) : mysqli_fetch_assoc($rs);
2874
}
2875
2876
function smf_mysql_fetch_row($rs)
2877
{
2878
	global $db_type;
2879
	return ($db_type == 'mysql') ? mysql_fetch_row($rs) : mysqli_fetch_row($rs);
2880
}
2881
2882
function smf_mysql_free_result($rs)
2883
{
2884
	global $db_type;
2885
	return ($db_type == 'mysql') ? mysql_free_result($rs) : mysqli_free_result($rs);
2886
}
2887
2888
function smf_mysql_insert_id($rs)
2889
{
2890
	global $db_type;
2891
	return ($db_type == 'mysql') ? mysql_insert_id($rs) : mysqli_insert_id($rs);
2892
}
2893
2894
function smf_mysql_num_rows($rs)
2895
{
2896
	global $db_type;
2897
	return ($db_type == 'mysql') ? mysql_num_rows($rs) : mysqli_num_rows($rs);
2898
}
2899
2900
function smf_mysql_real_escape_string($string)
2901
{
2902
	global $db_type, $db_connection;
2903
	return ($db_type == 'mysql') ? mysql_real_escape_string($string, $db_connection) : mysqli_real_escape_string($db_connection, $string);
2904
}
2905
2906
// This performs a table alter, but does it unbuffered so the script can time out professionally.
2907
function protected_alter($change, $substep, $is_test = false)
2908
{
2909
	global $db_prefix, $smcFunc;
2910
2911
	db_extend('packages');
2912
2913
	// Firstly, check whether the current index/column exists.
2914
	$found = false;
2915
	if ($change['type'] === 'column')
2916
	{
2917
		$columns = $smcFunc['db_list_columns']('{db_prefix}' . $change['table'], true);
2918
		foreach ($columns as $column)
2919
		{
2920
			// Found it?
2921
			if ($column['name'] === $change['name'])
2922
			{
2923
				$found |= 1;
2924
				// Do some checks on the data if we have it set.
2925
				if (isset($change['col_type']))
2926
					$found &= $change['col_type'] === $column['type'];
2927
				if (isset($change['null_allowed']))
2928
					$found &= $column['null'] == $change['null_allowed'];
2929
				if (isset($change['default']))
2930
					$found &= $change['default'] === $column['default'];
2931
			}
2932
		}
2933
	}
2934
	elseif ($change['type'] === 'index')
2935
	{
2936
		$request = upgrade_query( '
2937
			SHOW INDEX
2938
			FROM ' . $db_prefix . $change['table']);
2939
		if ($request !== false)
2940
		{
2941
			$cur_index = array();
2942
2943
			while ($row = $smcFunc['db_fetch_assoc']($request))
2944
				if ($row['Key_name'] === $change['name'])
2945
					$cur_index[(int) $row['Seq_in_index']] = $row['Column_name'];
2946
2947
			ksort($cur_index, SORT_NUMERIC);
2948
			$found = array_values($cur_index) === $change['target_columns'];
2949
2950
			$smcFunc['db_free_result']($request);
2951
		}
2952
	}
2953
2954
	// If we're trying to add and it's added, we're done.
2955
	if ($found && in_array($change['method'], array('add', 'change')))
2956
		return true;
2957
	// Otherwise if we're removing and it wasn't found we're also done.
2958
	elseif (!$found && in_array($change['method'], array('remove', 'change_remove')))
2959
		return true;
2960
	// Otherwise is it just a test?
2961
	elseif ($is_test)
2962
		return false;
2963
2964
	// Not found it yet? Bummer! How about we see if we're currently doing it?
2965
	$running = false;
2966
	$found = false;
2967
	while (1 == 1)
2968
	{
2969
		$request = upgrade_query('
2970
			SHOW FULL PROCESSLIST');
2971
		while ($row = $smcFunc['db_fetch_assoc']($request))
2972
		{
2973
			if (strpos($row['Info'], 'ALTER TABLE ' . $db_prefix . $change['table']) !== false && strpos($row['Info'], $change['text']) !== false)
2974
				$found = true;
2975
		}
2976
2977
		// Can't find it? Then we need to run it fools!
2978
		if (!$found && !$running)
2979
		{
2980
			$smcFunc['db_free_result']($request);
2981
2982
			$success = upgrade_query('
2983
				ALTER TABLE ' . $db_prefix . $change['table'] . '
2984
				' . $change['text'], true) !== false;
2985
2986
			if (!$success)
2987
				return false;
2988
2989
			// Return
2990
			$running = true;
2991
		}
2992
		// What if we've not found it, but we'd ran it already? Must of completed.
2993
		elseif (!$found)
2994
		{
2995
			$smcFunc['db_free_result']($request);
2996
			return true;
2997
		}
2998
2999
		// Pause execution for a sec or three.
3000
		sleep(3);
3001
3002
		// Can never be too well protected.
3003
		nextSubstep($substep);
3004
	}
3005
3006
	// Protect it.
3007
	nextSubstep($substep);
3008
}
3009
3010
// Alter a text column definition preserving its character set.
3011
function textfield_alter($change, $substep)
3012
{
3013
	global $db_prefix, $databases, $db_type, $smcFunc;
3014
3015
	// Versions of MySQL < 4.1 wouldn't benefit from character set detection.
3016
	if (empty($databases[$db_type]['utf8_support']) || version_compare($databases[$db_type]['utf8_version'], eval($databases[$db_type]['utf8_version_check']), '>'))
3017
	{
3018
		$column_fix = true;
3019
		$null_fix = !$change['null_allowed'];
3020
	}
3021
	else
3022
	{
3023
		$request = $smcFunc['db_query']('', '
3024
			SHOW FULL COLUMNS
3025
			FROM {db_prefix}' . $change['table'] . '
3026
			LIKE {string:column}',
3027
			array(
3028
				'column' => $change['column'],
3029
				'db_error_skip' => true,
3030
			)
3031
		);
3032
		if ($smcFunc['db_num_rows']($request) === 0)
3033
			die('Unable to find column ' . $change['column'] . ' inside table ' . $db_prefix . $change['table']);
3034
		$table_row = $smcFunc['db_fetch_assoc']($request);
3035
		$smcFunc['db_free_result']($request);
3036
3037
		// If something of the current column definition is different, fix it.
3038
		$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']);
3039
3040
		// Columns that previously allowed null, need to be converted first.
3041
		$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
3042
3043
		// Get the character set that goes with the collation of the column.
3044 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...
3045
		{
3046
			$request = $smcFunc['db_query']('', '
3047
				SHOW COLLATION
3048
				LIKE {string:collation}',
3049
				array(
3050
					'collation' => $table_row['Collation'],
3051
					'db_error_skip' => true,
3052
				)
3053
			);
3054
			// No results? Just forget it all together.
3055
			if ($smcFunc['db_num_rows']($request) === 0)
3056
				unset($table_row['Collation']);
3057
			else
3058
				$collation_info = $smcFunc['db_fetch_assoc']($request);
3059
			$smcFunc['db_free_result']($request);
3060
		}
3061
	}
3062
3063
	if ($column_fix)
3064
	{
3065
		// Make sure there are no NULL's left.
3066
		if ($null_fix)
3067
			$smcFunc['db_query']('', '
3068
				UPDATE {db_prefix}' . $change['table'] . '
3069
				SET ' . $change['column'] . ' = {string:default}
3070
				WHERE ' . $change['column'] . ' IS NULL',
3071
				array(
3072
					'default' => isset($change['default']) ? $change['default'] : '',
3073
					'db_error_skip' => true,
3074
				)
3075
			);
3076
3077
		// Do the actual alteration.
3078
		$smcFunc['db_query']('', '
3079
			ALTER TABLE {db_prefix}' . $change['table'] . '
3080
			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}' : ''),
3081
			array(
3082
				'default' => isset($change['default']) ? $change['default'] : '',
3083
				'db_error_skip' => true,
3084
			)
3085
		);
3086
	}
3087
	nextSubstep($substep);
3088
}
3089
3090
// Check if we need to alter this query.
3091
function checkChange(&$change)
3092
{
3093
	global $smcFunc, $db_type, $databases;
3094
	static $database_version, $where_field_support;
3095
3096
	// Attempt to find a database_version.
3097
	if (empty($database_version))
3098
	{
3099
		$database_version = $databases[$db_type]['version_check'];
3100
		$where_field_support = ($db_type == 'mysql' || $db_type == 'mysqli') && version_compare('5.0', $database_version, '<=');
3101
	}
3102
3103
	// Not a column we need to check on?
3104
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
3105
		return;
3106
3107
	// Break it up you (six|seven).
3108
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
3109
3110
	// Can we support a shortcut method?
3111
	if ($where_field_support)
3112
	{
3113
		// Get the details about this change.
3114
		$request = $smcFunc['db_query']('', '
3115
			SHOW FIELDS
3116
			FROM {db_prefix}{raw:table}
3117
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
3118
			array(
3119
				'table' => $change['table'],
3120
				'old_name' => $temp[1],
3121
				'new_name' => $temp[2],
3122
		));
3123
		if ($smcFunc['db_num_rows'] != 1)
3124
			return;
3125
3126
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
3127
		$smcFunc['db_free_result']($request);
3128
	}
3129
	else
3130
	{
3131
		// Do this the old fashion, sure method way.
3132
		$request = $smcFunc['db_query']('', '
3133
			SHOW FIELDS
3134
			FROM {db_prefix}{raw:table}',
3135
			array(
3136
				'table' => $change['table'],
3137
		));
3138
		// Mayday!
3139
		if ($smcFunc['db_num_rows'] == 0)
3140
			return;
3141
3142
		// Oh where, oh where has my little field gone. Oh where can it be...
3143
		while ($row = $smcFunc['db_query']($request))
3144
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
3145
			{
3146
				$current_type = $row['Type'];
3147
				break;
3148
			}
3149
	}
3150
3151
	// If this doesn't match, the column may of been altered for a reason.
3152
	if (trim($current_type) != trim($temp[3]))
3153
		$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...
3154
3155
	// Piece this back together.
3156
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
3157
}
3158
3159
// The next substep.
3160
function nextSubstep($substep)
3161
{
3162
	global $start_time, $timeLimitThreshold, $command_line, $custom_warning;
3163
	global $step_progress, $is_debug, $upcontext;
3164
3165
	if ($_GET['substep'] < $substep)
3166
		$_GET['substep'] = $substep;
3167
3168
	if ($command_line)
3169
	{
3170
		if (time() - $start_time > 1 && empty($is_debug))
3171
		{
3172
			echo '.';
3173
			$start_time = time();
3174
		}
3175
		return;
3176
	}
3177
3178
	@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...
3179
	if (function_exists('apache_reset_timeout'))
3180
		@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...
3181
3182
	if (time() - $start_time <= $timeLimitThreshold)
3183
		return;
3184
3185
	// Do we have some custom step progress stuff?
3186
	if (!empty($step_progress))
3187
	{
3188
		$upcontext['substep_progress'] = 0;
3189
		$upcontext['substep_progress_name'] = $step_progress['name'];
3190
		if ($step_progress['current'] > $step_progress['total'])
3191
			$upcontext['substep_progress'] = 99.9;
3192
		else
3193
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
3194
3195
		// Make it nicely rounded.
3196
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
3197
	}
3198
3199
	// If this is XML we just exit right away!
3200
	if (isset($_GET['xml']))
3201
		return upgradeExit();
3202
3203
	// We're going to pause after this!
3204
	$upcontext['pause'] = true;
3205
3206
	$upcontext['query_string'] = '';
3207
	foreach ($_GET as $k => $v)
3208
	{
3209
		if ($k != 'data' && $k != 'substep' && $k != 'step')
3210
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
3211
	}
3212
3213
	// Custom warning?
3214
	if (!empty($custom_warning))
3215
		$upcontext['custom_warning'] = $custom_warning;
3216
3217
	upgradeExit();
3218
}
3219
3220
function cmdStep0()
3221
{
3222
	global $boarddir, $sourcedir, $language, $modSettings, $start_time, $cachedir, $databases, $db_type, $smcFunc, $upcontext;
3223
	global $language, $is_debug;
3224
	$start_time = time();
3225
3226
	ob_end_clean();
3227
	ob_implicit_flush(true);
3228
	@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...
3229
3230
	if (!isset($_SERVER['argv']))
3231
		$_SERVER['argv'] = array();
3232
	$_GET['maint'] = 1;
3233
3234
	foreach ($_SERVER['argv'] as $i => $arg)
3235
	{
3236
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
3237
			$_GET['lang'] = $match[1];
3238
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
3239
			continue;
3240
		elseif ($arg == '--no-maintenance')
3241
			$_GET['maint'] = 0;
3242
		elseif ($arg == '--debug')
3243
			$is_debug = true;
3244
		elseif ($arg == '--backup')
3245
			$_POST['backup'] = 1;
3246
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
3247
			$_GET['conv'] = 1;
3248
		elseif ($i != 0)
3249
		{
3250
			echo 'SMF Command-line Upgrader
3251
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
3252
3253
    --language=LANG         Reset the forum\'s language to LANG.
3254
    --no-maintenance        Don\'t put the forum into maintenance mode.
3255
    --debug                 Output debugging information.
3256
    --backup                Create backups of tables with "backup_" prefix.';
3257
			echo "\n";
3258
			exit;
3259
		}
3260
	}
3261
3262
	if (!php_version_check())
3263
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
3264
	if (!db_version_check())
3265
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
3266
3267
	// Do some checks to make sure they have proper privileges
3268
	db_extend('packages');
3269
3270
	// CREATE
3271
	$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');
3272
3273
	// ALTER
3274
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'tinytext', 'null' => false, 'default' => ''));
3275
3276
	// DROP
3277
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
3278
3279
	// Sorry... we need CREATE, ALTER and DROP
3280 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...
3281
		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);
3282
3283
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
3284
		&& @file_exists($sourcedir . '/QueryString.php')
3285
		&& @file_exists($sourcedir . '/ManageBoards.php');
3286
	if (!$check && !isset($modSettings['smfVersion']))
3287
		print_error('Error: Some files are missing or out-of-date.', true);
3288
3289
	// Do a quick version spot check.
3290
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
3291
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
3292
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
3293
		print_error('Error: Some files have not yet been updated properly.');
3294
3295
	// Make sure Settings.php is writable.
3296
		quickFileWritable($boarddir . '/Settings.php');
3297
	if (!is_writable($boarddir . '/Settings.php'))
3298
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
3299
3300
	// Make sure Settings_bak.php is writable.
3301
		quickFileWritable($boarddir . '/Settings_bak.php');
3302
	if (!is_writable($boarddir . '/Settings_bak.php'))
3303
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
3304
3305 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...
3306
		print_error('Error: Unable to obtain write access to "agreement.txt".');
3307
	elseif (isset($modSettings['agreement']))
3308
	{
3309
		$fp = fopen($boarddir . '/agreement.txt', 'w');
3310
		fwrite($fp, $modSettings['agreement']);
3311
		fclose($fp);
3312
	}
3313
3314
	// Make sure Themes is writable.
3315
	quickFileWritable($modSettings['theme_dir']);
3316
3317
	if (!is_writable($modSettings['theme_dir']) && !isset($modSettings['smfVersion']))
3318
		print_error('Error: Unable to obtain write access to "Themes".');
3319
3320
	// Make sure cache directory exists and is writable!
3321
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
3322
	if (!file_exists($cachedir_temp))
3323
		@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...
3324
3325
	// Make sure the cache temp dir is writable.
3326
	quickFileWritable($cachedir_temp);
3327
3328
	if (!is_writable($cachedir_temp))
3329
		print_error('Error: Unable to obtain write access to "cache".', true);
3330
3331
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
3332
		print_error('Error: Unable to find language files!', true);
3333
	else
3334
	{
3335
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
3336
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
3337
3338
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
3339
			print_error('Error: Language files out of date.', true);
3340
		if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
3341
			print_error('Error: Install language is missing for selected language.', true);
3342
3343
		// Otherwise include it!
3344
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
3345
	}
3346
3347
	// Make sure we skip the HTML for login.
3348
	$_POST['upcont'] = true;
3349
	$upcontext['current_step'] = 1;
3350
}
3351
3352
function print_error($message, $fatal = false)
3353
{
3354
	static $fp = null;
3355
3356
	if ($fp === null)
3357
		$fp = fopen('php://stderr', 'wb');
3358
3359
	fwrite($fp, $message . "\n");
3360
3361
	if ($fatal)
3362
		exit;
3363
}
3364
3365
function throw_error($message)
3366
{
3367
	global $upcontext;
3368
3369
	$upcontext['error_msg'] = $message;
3370
	$upcontext['sub_template'] = 'error_message';
3371
3372
	return false;
3373
}
3374
3375
// Check files are writable - make them writable if necessary...
3376
function makeFilesWritable(&$files)
3377
{
3378
	global $upcontext, $boarddir;
3379
3380
	if (empty($files))
3381
		return true;
3382
3383
	$failure = false;
3384
	// On linux, it's easy - just use is_writable!
3385
	if (substr(__FILE__, 1, 2) != ':\\')
3386
	{
3387
		$upcontext['systemos'] = 'linux';
3388
3389
		foreach ($files as $k => $file)
3390
		{
3391
			if (!is_writable($file))
3392
			{
3393
				@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...
3394
3395
				// Well, 755 hopefully worked... if not, try 777.
3396
				if (!is_writable($file) && !@chmod($file, 0777))
3397
					$failure = true;
3398
				// Otherwise remove it as it's good!
3399
				else
3400
					unset($files[$k]);
3401
			}
3402
			else
3403
				unset($files[$k]);
3404
		}
3405
	}
3406
	// Windows is trickier.  Let's try opening for r+...
3407
	else
3408
	{
3409
		$upcontext['systemos'] = 'windows';
3410
3411
		foreach ($files as $k => $file)
3412
		{
3413
			// Folders can't be opened for write... but the index.php in them can ;).
3414
			if (is_dir($file))
3415
				$file .= '/index.php';
3416
3417
			// Funny enough, chmod actually does do something on windows - it removes the read only attribute.
3418
			@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...
3419
			$fp = @fopen($file, 'r+');
3420
3421
			// Hmm, okay, try just for write in that case...
3422
			if (!$fp)
3423
				$fp = @fopen($file, 'w');
3424
3425
			if (!$fp)
3426
				$failure = true;
3427
			else
3428
				unset($files[$k]);
3429
			@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...
3430
		}
3431
	}
3432
3433
	if (empty($files))
3434
		return true;
3435
3436
	if (!isset($_SERVER))
3437
		return !$failure;
3438
3439
	// What still needs to be done?
3440
	$upcontext['chmod']['files'] = $files;
3441
3442
	// If it's windows it's a mess...
3443
	if ($failure && substr(__FILE__, 1, 2) == ':\\')
3444
	{
3445
		$upcontext['chmod']['ftp_error'] = 'total_mess';
3446
3447
		return false;
3448
	}
3449
	// We're going to have to use... FTP!
3450
	elseif ($failure)
3451
	{
3452
		// Load any session data we might have...
3453
		if (!isset($_POST['ftp_username']) && isset($_SESSION['installer_temp_ftp']))
3454
		{
3455
			$upcontext['chmod']['server'] = $_SESSION['installer_temp_ftp']['server'];
3456
			$upcontext['chmod']['port'] = $_SESSION['installer_temp_ftp']['port'];
3457
			$upcontext['chmod']['username'] = $_SESSION['installer_temp_ftp']['username'];
3458
			$upcontext['chmod']['password'] = $_SESSION['installer_temp_ftp']['password'];
3459
			$upcontext['chmod']['path'] = $_SESSION['installer_temp_ftp']['path'];
3460
		}
3461
		// Or have we submitted?
3462 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...
3463
		{
3464
			$upcontext['chmod']['server'] = $_POST['ftp_server'];
3465
			$upcontext['chmod']['port'] = $_POST['ftp_port'];
3466
			$upcontext['chmod']['username'] = $_POST['ftp_username'];
3467
			$upcontext['chmod']['password'] = $_POST['ftp_password'];
3468
			$upcontext['chmod']['path'] = $_POST['ftp_path'];
3469
		}
3470
3471
		if (isset($upcontext['chmod']['username']))
3472
		{
3473
			$ftp = new ftp_connection($upcontext['chmod']['server'], $upcontext['chmod']['port'], $upcontext['chmod']['username'], $upcontext['chmod']['password']);
3474
3475
			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...
3476
			{
3477
				// Try it without /home/abc just in case they messed up.
3478
				if (!$ftp->chdir($upcontext['chmod']['path']))
3479
				{
3480
					$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...
3481
					$ftp->chdir(preg_replace('~^/home[2]?/[^/]+?~', '', $upcontext['chmod']['path']));
3482
				}
3483
			}
3484
		}
3485
3486
		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...
3487
		{
3488
			if (!isset($ftp))
3489
				$ftp = new ftp_connection(null);
3490
			// Save the error so we can mess with listing...
3491
			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...
3492
				$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...
3493
3494
			list ($username, $detect_path, $found_path) = $ftp->detect_path(dirname(__FILE__));
3495
3496
			if ($found_path || !isset($upcontext['chmod']['path']))
3497
				$upcontext['chmod']['path'] = $detect_path;
3498
3499
			if (!isset($upcontext['chmod']['username']))
3500
				$upcontext['chmod']['username'] = $username;
3501
3502
			return false;
3503
		}
3504
		else
3505
		{
3506
			// We want to do a relative path for FTP.
3507
			if (!in_array($upcontext['chmod']['path'], array('', '/')))
3508
			{
3509
				$ftp_root = strtr($boarddir, array($upcontext['chmod']['path'] => ''));
3510
				if (substr($ftp_root, -1) == '/' && ($upcontext['chmod']['path'] == '' || $upcontext['chmod']['path'][0] === '/'))
3511
				$ftp_root = substr($ftp_root, 0, -1);
3512
			}
3513
			else
3514
				$ftp_root = $boarddir;
3515
3516
			// Save the info for next time!
3517
			$_SESSION['installer_temp_ftp'] = array(
3518
				'server' => $upcontext['chmod']['server'],
3519
				'port' => $upcontext['chmod']['port'],
3520
				'username' => $upcontext['chmod']['username'],
3521
				'password' => $upcontext['chmod']['password'],
3522
				'path' => $upcontext['chmod']['path'],
3523
				'root' => $ftp_root,
3524
			);
3525
3526
			foreach ($files as $k => $file)
3527
			{
3528
				if (!is_writable($file))
3529
					$ftp->chmod($file, 0755);
3530
				if (!is_writable($file))
3531
					$ftp->chmod($file, 0777);
3532
3533
				// Assuming that didn't work calculate the path without the boarddir.
3534
				if (!is_writable($file))
3535
				{
3536
					if (strpos($file, $boarddir) === 0)
3537
					{
3538
						$ftp_file = strtr($file, array($_SESSION['installer_temp_ftp']['root'] => ''));
3539
						$ftp->chmod($ftp_file, 0755);
3540
						if (!is_writable($file))
3541
							$ftp->chmod($ftp_file, 0777);
3542
						// Sometimes an extra slash can help...
3543
						$ftp_file = '/' . $ftp_file;
3544
						if (!is_writable($file))
3545
							$ftp->chmod($ftp_file, 0755);
3546
						if (!is_writable($file))
3547
							$ftp->chmod($ftp_file, 0777);
3548
					}
3549
				}
3550
3551
				if (is_writable($file))
3552
					unset($files[$k]);
3553
			}
3554
3555
			$ftp->close();
3556
		}
3557
	}
3558
3559
	// What remains?
3560
	$upcontext['chmod']['files'] = $files;
3561
3562
	if (empty($files))
3563
		return true;
3564
3565
	return false;
3566
}
3567
3568
function quickFileWritable($file)
3569
{
3570
	if (is_writable($file))
3571
		return true;
3572
3573
	@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...
3574
3575
	// Try 755 and 775 first since 777 doesn't always work and could be a risk...
3576
	$chmod_values = array(0755, 0775, 0777);
3577
3578
	foreach($chmod_values as $val)
3579
	{
3580
		// If it's writable, break out of the loop
3581
		if (is_writable($file))
3582
			break;
3583
		else
3584
			@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...
3585
	}
3586
}
3587
function smf_strtolower($string)
3588
{
3589
	global $sourcedir;
3590
	if (function_exists('mb_strtolower'))
3591
		return mb_strtolower($string, 'UTF-8');
3592
	require_once($sourcedir . '/Subs-Charset.php');
3593
	return utf8_strtolower($string);
3594
}
3595
3596
/**
3597
 * Handles converting your database to UTF-8
3598
 */
3599
function convertUtf8()
3600
{
3601
	global $upcontext, $db_character_set, $sourcedir, $smcFunc, $modSettings, $language, $db_prefix, $db_type, $command_line, $support_js, $is_debug;
3602
3603
	// First make sure they aren't already on UTF-8 before we go anywhere...
3604
	if ($db_type == 'postgresql' || ($db_character_set === 'utf8' && !empty($modSettings['global_character_set']) && $modSettings['global_character_set'] === 'UTF-8'))
3605
	{
3606
		return true;
3607
	}
3608
	else
3609
	{
3610
		$upcontext['page_title'] = 'Converting to UTF8';
3611
		$upcontext['sub_template'] = isset($_GET['xml']) ? 'convert_xml' : 'convert_utf8';
3612
3613
		// The character sets used in SMF's language files with their db equivalent.
3614
		$charsets = array(
3615
			// Armenian
3616
			'armscii8' => 'armscii8',
3617
			// Chinese-traditional.
3618
			'big5' => 'big5',
3619
			// Chinese-simplified.
3620
			'gbk' => 'gbk',
3621
			// West European.
3622
			'ISO-8859-1' => 'latin1',
3623
			// Romanian.
3624
			'ISO-8859-2' => 'latin2',
3625
			// Turkish.
3626
			'ISO-8859-9' => 'latin5',
3627
			// Latvian
3628
			'ISO-8859-13' => 'latin7',
3629
			// West European with Euro sign.
3630
			'ISO-8859-15' => 'latin9',
3631
			// Thai.
3632
			'tis-620' => 'tis620',
3633
			// Persian, Chinese, etc.
3634
			'UTF-8' => 'utf8',
3635
			// Russian.
3636
			'windows-1251' => 'cp1251',
3637
			// Greek.
3638
			'windows-1253' => 'utf8',
3639
			// Hebrew.
3640
			'windows-1255' => 'utf8',
3641
			// Arabic.
3642
			'windows-1256' => 'cp1256',
3643
		);
3644
3645
		// Get a list of character sets supported by your MySQL server.
3646
		$request = $smcFunc['db_query']('', '
3647
			SHOW CHARACTER SET',
3648
			array(
3649
			)
3650
		);
3651
		$db_charsets = array();
3652
		while ($row = $smcFunc['db_fetch_assoc']($request))
3653
			$db_charsets[] = $row['Charset'];
3654
3655
		$smcFunc['db_free_result']($request);
3656
3657
		// Character sets supported by both MySQL and SMF's language files.
3658
		$charsets = array_intersect($charsets, $db_charsets);
3659
3660
		// Use the messages.body column as indicator for the database charset.
3661
		$request = $smcFunc['db_query']('', '
3662
			SHOW FULL COLUMNS
3663
			FROM {db_prefix}messages
3664
			LIKE {string:body_like}',
3665
			array(
3666
				'body_like' => 'body',
3667
			)
3668
		);
3669
		$column_info = $smcFunc['db_fetch_assoc']($request);
3670
		$smcFunc['db_free_result']($request);
3671
3672
		// A collation looks like latin1_swedish. We only need the character set.
3673
		list($upcontext['database_charset']) = explode('_', $column_info['Collation']);
3674
		$upcontext['database_charset'] = in_array($upcontext['database_charset'], $charsets) ? array_search($upcontext['database_charset'], $charsets) : $upcontext['database_charset'];
3675
3676
		// Detect whether a fulltext index is set.
3677
		$request = $smcFunc['db_query']('', '
3678
 			SHOW INDEX
3679
	  	    FROM {db_prefix}messages',
3680
			array(
3681
			)
3682
		);
3683
3684
		$upcontext['dropping_index'] = false;
3685
3686
		// If there's a fulltext index, we need to drop it first...
3687 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...
3688
		{
3689
			while ($row = $smcFunc['db_fetch_assoc']($request))
3690
				if ($row['Column_name'] == 'body' && (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT' || isset($row['Comment']) && $row['Comment'] == 'FULLTEXT'))
3691
					$upcontext['fulltext_index'][] = $row['Key_name'];
3692
			$smcFunc['db_free_result']($request);
3693
3694
			if (isset($upcontext['fulltext_index']))
3695
				$upcontext['fulltext_index'] = array_unique($upcontext['fulltext_index']);
3696
		}
3697
3698
		// Drop it and make a note...
3699
		if (!empty($upcontext['fulltext_index']))
3700
		{
3701
			$upcontext['dropping_index'] = true;
3702
3703
			$smcFunc['db_query']('', '
3704
  			ALTER TABLE {db_prefix}messages
3705
	  		DROP INDEX ' . implode(',
3706
		  	DROP INDEX ', $upcontext['fulltext_index']),
3707
				array(
3708
					'db_error_skip' => true,
3709
				)
3710
			);
3711
3712
			// Update the settings table
3713
			$smcFunc['db_insert']('replace',
3714
				'{db_prefix}settings',
3715
				array('variable' => 'string', 'value' => 'string'),
3716
				array('db_search_index', ''),
3717
				array('variable')
3718
			);
3719
		}
3720
3721
		// Figure out what charset we should be converting from...
3722
		$lang_charsets = array(
3723
			'arabic' => 'windows-1256',
3724
			'armenian_east' => 'armscii-8',
3725
			'armenian_west' => 'armscii-8',
3726
			'azerbaijani_latin' => 'ISO-8859-9',
3727
			'bangla' => 'UTF-8',
3728
			'belarusian' => 'ISO-8859-5',
3729
			'bulgarian' => 'windows-1251',
3730
			'cambodian' => 'UTF-8',
3731
			'chinese_simplified' => 'gbk',
3732
			'chinese_traditional' => 'big5',
3733
			'croation' => 'ISO-8859-2',
3734
			'czech' => 'ISO-8859-2',
3735
			'czech_informal' => 'ISO-8859-2',
3736
			'english_pirate' => 'UTF-8',
3737
			'esperanto' => 'ISO-8859-3',
3738
			'estonian' => 'ISO-8859-15',
3739
			'filipino_tagalog' => 'UTF-8',
3740
			'filipino_vasayan' => 'UTF-8',
3741
			'georgian' => 'UTF-8',
3742
			'greek' => 'ISO-8859-3',
3743
			'hebrew' => 'windows-1255',
3744
			'hungarian' => 'ISO-8859-2',
3745
			'irish' => 'UTF-8',
3746
			'japanese' => 'UTF-8',
3747
			'khmer' => 'UTF-8',
3748
			'korean' => 'UTF-8',
3749
			'kurdish_kurmanji' => 'ISO-8859-9',
3750
			'kurdish_sorani' => 'windows-1256',
3751
			'lao' => 'tis-620',
3752
			'latvian' => 'ISO-8859-13',
3753
			'lithuanian' => 'ISO-8859-4',
3754
			'macedonian' => 'UTF-8',
3755
			'malayalam' => 'UTF-8',
3756
			'mongolian' => 'UTF-8',
3757
			'nepali' => 'UTF-8',
3758
			'persian' => 'UTF-8',
3759
			'polish' => 'ISO-8859-2',
3760
			'romanian' => 'ISO-8859-2',
3761
			'russian' => 'windows-1252',
3762
			'sakha' => 'UTF-8',
3763
			'serbian_cyrillic' => 'ISO-8859-5',
3764
			'serbian_latin' => 'ISO-8859-2',
3765
			'sinhala' => 'UTF-8',
3766
			'slovak' => 'ISO-8859-2',
3767
			'slovenian' => 'ISO-8859-2',
3768
			'telugu' => 'UTF-8',
3769
			'thai' => 'tis-620',
3770
			'turkish' => 'ISO-8859-9',
3771
			'turkmen' => 'ISO-8859-9',
3772
			'ukranian' => 'windows-1251',
3773
			'urdu' => 'UTF-8',
3774
			'uzbek_cyrillic' => 'ISO-8859-5',
3775
			'uzbek_latin' => 'ISO-8859-5',
3776
			'vietnamese' => 'UTF-8',
3777
			'yoruba' => 'UTF-8'
3778
		);
3779
3780
		// Default to ISO-8859-1 unless we detected another supported charset
3781
		$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';
3782
3783
		$upcontext['charset_list'] = array_keys($charsets);
3784
3785
		// Translation table for the character sets not native for MySQL.
3786
		$translation_tables = array(
3787
			'windows-1255' => array(
3788
				'0x81' => '\'\'',		'0x8A' => '\'\'',		'0x8C' => '\'\'',
3789
				'0x8D' => '\'\'',		'0x8E' => '\'\'',		'0x8F' => '\'\'',
3790
				'0x90' => '\'\'',		'0x9A' => '\'\'',		'0x9C' => '\'\'',
3791
				'0x9D' => '\'\'',		'0x9E' => '\'\'',		'0x9F' => '\'\'',
3792
				'0xCA' => '\'\'',		'0xD9' => '\'\'',		'0xDA' => '\'\'',
3793
				'0xDB' => '\'\'',		'0xDC' => '\'\'',		'0xDD' => '\'\'',
3794
				'0xDE' => '\'\'',		'0xDF' => '\'\'',		'0xFB' => '\'\'',
3795
				'0xFC' => '\'\'',		'0xFF' => '\'\'',		'0xC2' => '0xFF',
3796
				'0x80' => '0xFC',		'0xE2' => '0xFB',		'0xA0' => '0xC2A0',
3797
				'0xA1' => '0xC2A1',		'0xA2' => '0xC2A2',		'0xA3' => '0xC2A3',
3798
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
3799
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
3800
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
3801
				'0xAF' => '0xC2AF',		'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',
3802
				'0xB2' => '0xC2B2',		'0xB3' => '0xC2B3',		'0xB4' => '0xC2B4',
3803
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
3804
				'0xB8' => '0xC2B8',		'0xB9' => '0xC2B9',		'0xBB' => '0xC2BB',
3805
				'0xBC' => '0xC2BC',		'0xBD' => '0xC2BD',		'0xBE' => '0xC2BE',
3806
				'0xBF' => '0xC2BF',		'0xD7' => '0xD7B3',		'0xD1' => '0xD781',
3807
				'0xD4' => '0xD7B0',		'0xD5' => '0xD7B1',		'0xD6' => '0xD7B2',
3808
				'0xE0' => '0xD790',		'0xEA' => '0xD79A',		'0xEC' => '0xD79C',
3809
				'0xED' => '0xD79D',		'0xEE' => '0xD79E',		'0xEF' => '0xD79F',
3810
				'0xF0' => '0xD7A0',		'0xF1' => '0xD7A1',		'0xF2' => '0xD7A2',
3811
				'0xF3' => '0xD7A3',		'0xF5' => '0xD7A5',		'0xF6' => '0xD7A6',
3812
				'0xF7' => '0xD7A7',		'0xF8' => '0xD7A8',		'0xF9' => '0xD7A9',
3813
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
3814
				'0x86' => '0xE280A0',	'0x87' => '0xE280A1',	'0x89' => '0xE280B0',
3815
				'0x8B' => '0xE280B9',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
3816
				'0x95' => '0xE280A2',	'0x97' => '0xE28094',	'0x99' => '0xE284A2',
3817
				'0xC0' => '0xD6B0',		'0xC1' => '0xD6B1',		'0xC3' => '0xD6B3',
3818
				'0xC4' => '0xD6B4',		'0xC5' => '0xD6B5',		'0xC6' => '0xD6B6',
3819
				'0xC7' => '0xD6B7',		'0xC8' => '0xD6B8',		'0xC9' => '0xD6B9',
3820
				'0xCB' => '0xD6BB',		'0xCC' => '0xD6BC',		'0xCD' => '0xD6BD',
3821
				'0xCE' => '0xD6BE',		'0xCF' => '0xD6BF',		'0xD0' => '0xD780',
3822
				'0xD2' => '0xD782',		'0xE3' => '0xD793',		'0xE4' => '0xD794',
3823
				'0xE5' => '0xD795',		'0xE7' => '0xD797',		'0xE9' => '0xD799',
3824
				'0xFD' => '0xE2808E',	'0xFE' => '0xE2808F',	'0x92' => '0xE28099',
3825
				'0x83' => '0xC692',		'0xD3' => '0xD783',		'0x88' => '0xCB86',
3826
				'0x98' => '0xCB9C',		'0x91' => '0xE28098',	'0x96' => '0xE28093',
3827
				'0xBA' => '0xC3B7',		'0x9B' => '0xE280BA',	'0xAA' => '0xC397',
3828
				'0xA4' => '0xE282AA',	'0xE1' => '0xD791',		'0xE6' => '0xD796',
3829
				'0xE8' => '0xD798',		'0xEB' => '0xD79B',		'0xF4' => '0xD7A4',
3830
				'0xFA' => '0xD7AA',		'0xFF' => '0xD6B2',		'0xFC' => '0xE282AC',
3831
				'0xFB' => '0xD792',
3832
			),
3833
			'windows-1253' => array(
3834
				'0x81' => '\'\'',			'0x88' => '\'\'',			'0x8A' => '\'\'',
3835
				'0x8C' => '\'\'',			'0x8D' => '\'\'',			'0x8E' => '\'\'',
3836
				'0x8F' => '\'\'',			'0x90' => '\'\'',			'0x98' => '\'\'',
3837
				'0x9A' => '\'\'',			'0x9C' => '\'\'',			'0x9D' => '\'\'',
3838
				'0x9E' => '\'\'',			'0x9F' => '\'\'',			'0xAA' => '\'\'',
3839
				'0xD2' => '\'\'',			'0xFF' => '\'\'',			'0xCE' => '0xCE9E',
3840
				'0xB8' => '0xCE88',		'0xBA' => '0xCE8A',		'0xBC' => '0xCE8C',
3841
				'0xBE' => '0xCE8E',		'0xBF' => '0xCE8F',		'0xC0' => '0xCE90',
3842
				'0xC8' => '0xCE98',		'0xCA' => '0xCE9A',		'0xCC' => '0xCE9C',
3843
				'0xCD' => '0xCE9D',		'0xCF' => '0xCE9F',		'0xDA' => '0xCEAA',
3844
				'0xE8' => '0xCEB8',		'0xEA' => '0xCEBA',		'0xEC' => '0xCEBC',
3845
				'0xEE' => '0xCEBE',		'0xEF' => '0xCEBF',		'0xC2' => '0xFF',
3846
				'0xBD' => '0xC2BD',		'0xED' => '0xCEBD',		'0xB2' => '0xC2B2',
3847
				'0xA0' => '0xC2A0',		'0xA3' => '0xC2A3',		'0xA4' => '0xC2A4',
3848
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
3849
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
3850
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
3851
				'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',		'0xB3' => '0xC2B3',
3852
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
3853
				'0xBB' => '0xC2BB',		'0xE2' => '0xCEB2',		'0x80' => '0xD2',
3854
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
3855
				'0x86' => '0xE280A0',	'0xA1' => '0xCE85',		'0xA2' => '0xCE86',
3856
				'0x87' => '0xE280A1',	'0x89' => '0xE280B0',	'0xB9' => '0xCE89',
3857
				'0x8B' => '0xE280B9',	'0x91' => '0xE28098',	'0x99' => '0xE284A2',
3858
				'0x92' => '0xE28099',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
3859
				'0x95' => '0xE280A2',	'0x96' => '0xE28093',	'0x97' => '0xE28094',
3860
				'0x9B' => '0xE280BA',	'0xAF' => '0xE28095',	'0xB4' => '0xCE84',
3861
				'0xC1' => '0xCE91',		'0xC3' => '0xCE93',		'0xC4' => '0xCE94',
3862
				'0xC5' => '0xCE95',		'0xC6' => '0xCE96',		'0x83' => '0xC692',
3863
				'0xC7' => '0xCE97',		'0xC9' => '0xCE99',		'0xCB' => '0xCE9B',
3864
				'0xD0' => '0xCEA0',		'0xD1' => '0xCEA1',		'0xD3' => '0xCEA3',
3865
				'0xD4' => '0xCEA4',		'0xD5' => '0xCEA5',		'0xD6' => '0xCEA6',
3866
				'0xD7' => '0xCEA7',		'0xD8' => '0xCEA8',		'0xD9' => '0xCEA9',
3867
				'0xDB' => '0xCEAB',		'0xDC' => '0xCEAC',		'0xDD' => '0xCEAD',
3868
				'0xDE' => '0xCEAE',		'0xDF' => '0xCEAF',		'0xE0' => '0xCEB0',
3869
				'0xE1' => '0xCEB1',		'0xE3' => '0xCEB3',		'0xE4' => '0xCEB4',
3870
				'0xE5' => '0xCEB5',		'0xE6' => '0xCEB6',		'0xE7' => '0xCEB7',
3871
				'0xE9' => '0xCEB9',		'0xEB' => '0xCEBB',		'0xF0' => '0xCF80',
3872
				'0xF1' => '0xCF81',		'0xF2' => '0xCF82',		'0xF3' => '0xCF83',
3873
				'0xF4' => '0xCF84',		'0xF5' => '0xCF85',		'0xF6' => '0xCF86',
3874
				'0xF7' => '0xCF87',		'0xF8' => '0xCF88',		'0xF9' => '0xCF89',
3875
				'0xFA' => '0xCF8A',		'0xFB' => '0xCF8B',		'0xFC' => '0xCF8C',
3876
				'0xFD' => '0xCF8D',		'0xFE' => '0xCF8E',		'0xFF' => '0xCE92',
3877
				'0xD2' => '0xE282AC',
3878
			),
3879
		);
3880
3881
		// Make some preparations.
3882
		if (isset($translation_tables[$upcontext['charset_detected']]))
3883
		{
3884
			$replace = '%field%';
3885
3886
			// Build a huge REPLACE statement...
3887
			foreach ($translation_tables[$upcontext['charset_detected']] as $from => $to)
3888
				$replace = 'REPLACE(' . $replace . ', ' . $from . ', ' . $to . ')';
3889
		}
3890
3891
		// Get a list of table names ahead of time... This makes it easier to set our substep and such
3892
		db_extend();
3893
		$queryTables = $smcFunc['db_list_tables'](false, $db_prefix);
3894
3895
		$upcontext['table_count'] = count($queryTables);
3896
		$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...
3897
3898
		for($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
3899
		{
3900
			$table = $queryTables[$_GET['substep']];
3901
3902
			// Do we need to pause?
3903
			nextSubstep($substep);
3904
3905
			$getTableStatus = $smcFunc['db_query']('', '
3906
				SHOW TABLE STATUS
3907
				LIKE {string:table_name}',
3908
				array(
3909
					'table_name' => str_replace('_', '\_', $table)
3910
				)
3911
			);
3912
3913
			// Only one row so we can just fetch_assoc and free the result...
3914
			$table_info = $smcFunc['db_fetch_assoc']($getTableStatus);
3915
			$smcFunc['db_free_result']($getTableStatus);
3916
3917
			$upcontext['cur_table_num'] = $_GET['substep'];
3918
			$upcontext['cur_table_name'] = $table_info['Name'];
3919
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3920
3921
			// Just to make sure it doesn't time out.
3922
			if (function_exists('apache_reset_timeout'))
3923
				@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...
3924
3925
			$table_charsets = array();
3926
3927
			// Loop through each column.
3928
			$queryColumns = $smcFunc['db_query']('', '
3929
				SHOW FULL COLUMNS
3930
				FROM ' . $table_info['Name'],
3931
				array(
3932
				)
3933
			);
3934
			while ($column_info = $smcFunc['db_fetch_assoc']($queryColumns))
3935
			{
3936
				// Only text'ish columns have a character set and need converting.
3937
				if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false)
3938
				{
3939
					$collation = empty($column_info['Collation']) || $column_info['Collation'] === 'NULL' ? $table_info['Collation'] : $column_info['Collation'];
3940
					if (!empty($collation) && $collation !== 'NULL')
3941
					{
3942
						list($charset) = explode('_', $collation);
3943
3944
						if (!isset($table_charsets[$charset]))
3945
							$table_charsets[$charset] = array();
3946
3947
						$table_charsets[$charset][] = $column_info;
3948
					}
3949
				}
3950
			}
3951
			$smcFunc['db_free_result']($queryColumns);
3952
3953
			// Only change the column if the data doesn't match the current charset.
3954
			if ((count($table_charsets) === 1 && key($table_charsets) !== $charsets[$upcontext['charset_detected']]) || count($table_charsets) > 1)
3955
			{
3956
				$updates_blob = '';
3957
				$updates_text = '';
3958
				foreach ($table_charsets as $charset => $columns)
3959
				{
3960
					if ($charset !== $charsets[$upcontext['charset_detected']])
3961
					{
3962
						foreach ($columns as $column)
3963
						{
3964
							$updates_blob .= '
3965
								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'] . '\'') . ',';
3966
							$updates_text .= '
3967
								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'] . '\'') . ',';
3968
						}
3969
					}
3970
				}
3971
3972
				// Change the columns to binary form.
3973
				$smcFunc['db_query']('', '
3974
					ALTER TABLE {raw:table_name}{raw:updates_blob}',
3975
					array(
3976
						'table_name' => $table_info['Name'],
3977
						'updates_blob' => substr($updates_blob, 0, -1),
3978
					)
3979
				);
3980
3981
				// Convert the character set if MySQL has no native support for it.
3982
				if (isset($translation_tables[$upcontext['charset_detected']]))
3983
				{
3984
					$update = '';
3985
					foreach ($table_charsets as $charset => $columns)
3986
						foreach ($columns as $column)
3987
							$update .= '
3988
								' . $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...
3989
3990
					$smcFunc['db_query']('', '
3991
						UPDATE {raw:table_name}
3992
						SET {raw:updates}',
3993
						array(
3994
							'table_name' => $table_info['Name'],
3995
							'updates' => substr($update, 0, -1),
3996
						)
3997
					);
3998
				}
3999
4000
				// Change the columns back, but with the proper character set.
4001
				$smcFunc['db_query']('', '
4002
					ALTER TABLE {raw:table_name}{raw:updates_text}',
4003
					array(
4004
						'table_name' => $table_info['Name'],
4005
						'updates_text' => substr($updates_text, 0, -1),
4006
					)
4007
				);
4008
			}
4009
4010
			// Now do the actual conversion (if still needed).
4011
			if ($charsets[$upcontext['charset_detected']] !== 'utf8')
4012
			{
4013
				if ($command_line)
4014
					echo 'Converting table ' . $table_info['Name'] . ' to UTF-8...';
4015
4016
				$smcFunc['db_query']('', '
4017
					ALTER TABLE {raw:table_name}
4018
					CONVERT TO CHARACTER SET utf8',
4019
						array(
4020
								'table_name' => $table_info['Name'],
4021
						)
4022
				);
4023
4024
				if ($command_line)
4025
					echo " done.\n";
4026
			}
4027
		}
4028
4029
		$prev_charset = empty($translation_tables[$upcontext['charset_detected']]) ? $charsets[$upcontext['charset_detected']] : $translation_tables[$upcontext['charset_detected']];
4030
4031
		$smcFunc['db_insert']('replace',
4032
			'{db_prefix}settings',
4033
			array('variable' => 'string', 'value' => 'string'),
4034
			array(array('global_character_set', 'UTF-8'), array('previousCharacterSet', $prev_charset)),
4035
			array('variable')
4036
		);
4037
4038
		// Store it in Settings.php too because it's needed before db connection.
4039
		// Hopefully this works...
4040
		require_once($sourcedir . '/Subs-Admin.php');
4041
		updateSettingsFile(array('db_character_set' => '\'utf8\''));
4042
4043
		// The conversion might have messed up some serialized strings. Fix them!
4044
		$request = $smcFunc['db_query']('', '
4045
			SELECT id_action, extra
4046
			FROM {db_prefix}log_actions
4047
			WHERE action IN ({string:remove}, {string:delete})',
4048
			array(
4049
				'remove' => 'remove',
4050
				'delete' => 'delete',
4051
			)
4052
		);
4053 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...
4054
		{
4055
			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)
4056
				$smcFunc['db_query']('', '
4057
					UPDATE {db_prefix}log_actions
4058
					SET extra = {string:extra}
4059
					WHERE id_action = {int:current_action}',
4060
					array(
4061
						'current_action' => $row['id_action'],
4062
						'extra' => $matches[1] . strlen($matches[3]) . ':"' . $matches[3] . '"' . $matches[4],
4063
					)
4064
				);
4065
		}
4066
		$smcFunc['db_free_result']($request);
4067
4068
		if ($upcontext['dropping_index'] && $command_line)
4069
		{
4070
			echo "\nYour fulltext search index was dropped to facilitate the conversion. You will need to recreate it.";
4071
			flush();
4072
		}
4073
	}
4074
4075
	return true;
4076
}
4077
4078
function serialize_to_json()
4079
{
4080
	global $command_line, $smcFunc, $modSettings, $sourcedir, $upcontext, $support_js, $is_debug;
4081
4082
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'serialize_json_xml' : 'serialize_json';
4083
	// First thing's first - did we already do this?
4084
	if (!empty($modSettings['json_done']))
4085
	{
4086
		if ($command_line)
4087
			return DeleteUpgrade();
4088
		else
4089
			return true;
4090
	}
4091
4092
	// Done it already - js wise?
4093
	if (!empty($_POST['json_done']))
4094
		return true;
4095
4096
	// List of tables affected by this function
4097
	// name => array('key', col1[,col2|true[,col3]])
4098
	// If 3rd item in array is true, it indicates that col1 could be empty...
4099
	$tables = array(
4100
		'background_tasks' => array('id_task', 'task_data'),
4101
		'log_actions' => array('id_action', 'extra'),
4102
		'log_online' => array('session', 'url'),
4103
		'log_packages' => array('id_install', 'db_changes', 'failed_steps', 'credits'),
4104
		'log_spider_hits' => array('id_hit', 'url'),
4105
		'log_subscribed' => array('id_sublog', 'pending_details'),
4106
		'pm_rules' => array('id_rule', 'criteria', 'actions'),
4107
		'qanda' => array('id_question', 'answers'),
4108
		'subscriptions' => array('id_subscribe', 'cost'),
4109
		'user_alerts' => array('id_alert', 'extra', true),
4110
		'user_drafts' => array('id_draft', 'to_list', true),
4111
		// These last two are a bit different - we'll handle those separately
4112
		'settings' => array(),
4113
		'themes' => array()
4114
	);
4115
4116
	// Set up some context stuff...
4117
	// Because we're not using numeric indices, we need this to figure out the current table name...
4118
	$keys = array_keys($tables);
4119
4120
	$upcontext['table_count'] = 13;
4121
	$upcontext['cur_table_num'] = $_GET['substep'];
4122
	$upcontext['cur_table_name'] = isset($keys[$_GET['substep']]) ? $keys[$_GET['substep']] : $keys[0];
4123
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
4124
	$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...
4125
4126 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...
4127
		if ($id < $_GET['substep'])
4128
			$upcontext['previous_tables'][] = $table;
4129
4130
	if ($command_line)
4131
		echo 'Converting data from serialize() to json_encode().';
4132
4133
	if (!$support_js || isset($_GET['xml']))
4134
	{
4135
		// Fix the data in each table
4136
		for ($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
4137
		{
4138
			$upcontext['cur_table_name'] = isset($keys[$substep + 1]) ? $keys[$substep + 1] : $keys[$substep];
4139
			$upcontext['cur_table_num'] = $substep + 1;
4140
4141
			$upcontext['step_progress'] = (int)(($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
4142
4143
			// Do we need to pause?
4144
			nextSubstep($substep);
4145
4146
			// Initialize a few things...
4147
			$where = '';
4148
			$vars = array();
4149
			$table = $keys[$substep];
4150
			$info = $tables[$table];
4151
4152
			// Now the fun - build our queries and all that fun stuff
4153
			if ($table == 'settings')
4154
			{
4155
				// Now a few settings...
4156
				$serialized_settings = array(
4157
					'attachment_basedirectories',
4158
					'attachmentUploadDir',
4159
					'cal_today_birthday',
4160
					'cal_today_event',
4161
					'cal_today_holiday',
4162
					'displayFields',
4163
					'last_attachments_directory',
4164
					'memberlist_cache',
4165
					'search_index_custom_config',
4166
					'spider_name_cache'
4167
				);
4168
4169
				// Loop through and fix these...
4170
				$new_settings = array();
4171
				if ($command_line)
4172
					echo "\n" . 'Fixing some settings...';
4173
4174
				foreach ($serialized_settings as $var)
4175
				{
4176
					if (isset($modSettings[$var]))
4177
					{
4178
						// Attempt to unserialize the setting
4179
						$temp = @safe_unserialize($modSettings[$var]);
4180
						if (!$temp && $command_line)
4181
							echo "\n - Failed to unserialize the '" . $var . "' setting. Skipping.";
4182
						elseif ($temp !== false)
4183
							$new_settings[$var] = json_encode($temp);
4184
					}
4185
				}
4186
4187
				// Update everything at once
4188
				if (!function_exists('cache_put_data'))
4189
					require_once($sourcedir . '/Load.php');
4190
				updateSettings($new_settings, true);
4191
4192
				if ($command_line)
4193
					echo ' done.';
4194
			}
4195
			elseif ($table == 'themes')
4196
			{
4197
				// Finally, fix the admin prefs. Unfortunately this is stored per theme, but hopefully they only have one theme installed at this point...
4198
				$query = $smcFunc['db_query']('', '
4199
					SELECT * FROM {db_prefix}themes
4200
					WHERE variable = {string:admin_prefs}',
4201
						array(
4202
							'admin_prefs' => 'admin_preferences'
4203
						)
4204
				);
4205
4206
				if ($smcFunc['db_num_rows']($query) != 0)
4207
				{
4208
					while ($row = $smcFunc['db_fetch_assoc']($query))
4209
					{
4210
						$temp = @safe_unserialize($row['admin_preferences']);
4211
4212
						if ($command_line)
4213
						{
4214
							if ($temp === false)
4215
								echo "\n" . 'Unserialize of admin_preferences for user ' . $row['id_member'] . ' failed. Skipping.';
4216
							else
4217
								echo "\n" . 'Fixing admin preferences...';
4218
						}
4219
4220
						if ($temp !== false)
4221
						{
4222
							$row['admin_preferences'] = json_encode($temp);
4223
4224
							// Even though we have all values from the table, UPDATE is still faster than REPLACE
4225
							$smcFunc['db_query']('', '
4226
								UPDATE {db_prefix}themes
4227
								SET value = {string:prefs}
4228
								WHERE id_theme = {int:theme}
4229
									AND id_member = {int:member}',
4230
								array(
4231
									'prefs' => $row['admin_preferences'],
4232
									'theme' => $row['id_theme'],
4233
									'member' => $row['id_member']
4234
								)
4235
							);
4236
4237
							if ($is_debug || $command_line)
4238
								echo ' done.';
4239
						}
4240
					}
4241
4242
					$smcFunc['db_free_result']($query);
4243
				}
4244
			}
4245
			else
4246
			{
4247
				// First item is always the key...
4248
				$key = $info[0];
4249
				unset($info[0]);
4250
4251
				// Now we know what columns we have and such...
4252
				if (count($info) == 2 && $info[2] === true)
4253
				{
4254
					$col_select = $info[1];
4255
					$where = ' WHERE ' . $info[1] . ' != {empty}';
4256
				}
4257
				else
4258
				{
4259
					$col_select = implode(', ', $info);
4260
				}
4261
4262
				$query = $smcFunc['db_query']('', '
4263
					SELECT ' . $key . ', ' . $col_select . '
4264
					FROM {db_prefix}' . $table . $where,
4265
					array()
4266
				);
4267
4268
				if ($smcFunc['db_num_rows']($query) != 0)
4269
				{
4270
					if ($command_line)
4271
					{
4272
						echo "\n" . ' +++ Fixing the "' . $table . '" table...';
4273
						flush();
4274
					}
4275
4276
					while ($row = $smcFunc['db_fetch_assoc']($query))
4277
					{
4278
						$update = '';
4279
4280
						// We already know what our key is...
4281
						foreach ($info as $col)
4282
						{
4283
							if ($col !== true && $row[$col] != '')
4284
							{
4285
								$temp = @safe_unserialize($row[$col]);
4286
4287
								if ($temp === false && $command_line)
4288
								{
4289
									echo "\nFailed to unserialize " . $row[$col] . "... Skipping\n";
4290
								}
4291
								else
4292
								{
4293
									$row[$col] = json_encode($temp);
4294
4295
									// Build our SET string and variables array
4296
									$update .= (empty($update) ? '' : ', ') . $col . ' = {string:' . $col . '}';
4297
									$vars[$col] = $row[$col];
4298
								}
4299
							}
4300
						}
4301
4302
						$vars[$key] = $row[$key];
4303
4304
						// In a few cases, we might have empty data, so don't try to update in those situations...
4305
						if (!empty($update))
4306
						{
4307
							$smcFunc['db_query']('', '
4308
								UPDATE {db_prefix}' . $table . '
4309
								SET ' . $update . '
4310
								WHERE ' . $key . ' = {' . ($key == 'session' ? 'string' : 'int') . ':' . $key . '}',
4311
								$vars
4312
							);
4313
						}
4314
					}
4315
4316
					if ($command_line)
4317
						echo ' done.';
4318
4319
					// Free up some memory...
4320
					$smcFunc['db_free_result']($query);
4321
				}
4322
			}
4323
			// If this is XML to keep it nice for the user do one table at a time anyway!
4324
			if (isset($_GET['xml']))
4325
				return upgradeExit();
4326
		}
4327
4328
		if ($command_line)
4329
		{
4330
			echo "\n" . 'Successful.' . "\n";
4331
			flush();
4332
		}
4333
		$upcontext['step_progress'] = 100;
4334
4335
		// Last but not least, insert a dummy setting so we don't have to do this again in the future...
4336
		updateSettings(array('json_done' => true));
4337
4338
		$_GET['substep'] = 0;
4339
		// Make sure we move on!
4340
		if ($command_line)
4341
			return DeleteUpgrade();
4342
4343
		return true;
4344
	}
4345
4346
	// If this fails we just move on to deleting the upgrade anyway...
4347
	$_GET['substep'] = 0;
4348
	return false;
4349
}
4350
4351
/******************************************************************************
4352
******************* Templates are below this point ****************************
4353
******************************************************************************/
4354
4355
// This is what is displayed if there's any chmod to be done. If not it returns nothing...
4356
function template_chmod()
4357
{
4358
	global $upcontext, $txt, $settings;
4359
4360
	// Don't call me twice!
4361
	if (!empty($upcontext['chmod_called']))
4362
		return;
4363
4364
	$upcontext['chmod_called'] = true;
4365
4366
	// Nothing?
4367
	if (empty($upcontext['chmod']['files']) && empty($upcontext['chmod']['ftp_error']))
4368
		return;
4369
4370
	// Was it a problem with Windows?
4371
	if (!empty($upcontext['chmod']['ftp_error']) && $upcontext['chmod']['ftp_error'] == 'total_mess')
4372
	{
4373
		echo '
4374
			<div class="error_message">
4375
				<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>
4376
				<ul style="margin: 2.5ex; font-family: monospace;">
4377
				<li>' . implode('</li>
4378
				<li>', $upcontext['chmod']['files']). '</li>
4379
			</ul>
4380
			</div>';
4381
4382
		return false;
4383
	}
4384
4385
	echo '
4386
		<div class="panel">
4387
			<h2>Your FTP connection information</h2>
4388
			<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>
4389
			<script>
4390
				function warning_popup()
4391
				{
4392
					popup = window.open(\'\',\'popup\',\'height=150,width=400,scrollbars=yes\');
4393
					var content = popup.document;
4394
					content.write(\'<!DOCTYPE html>\n\');
4395
					content.write(\'<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>\n\t<head>\n\t\t<meta name="robots" content="noindex">\n\t\t\');
4396
					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\');
4397
					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\');
4398
					content.write(\'<p>', implode('<br>\n\t\t\t', $upcontext['chmod']['files']), '</p>\n\t\t\t\');';
4399
4400
	if (isset($upcontext['systemos']) && $upcontext['systemos'] == 'linux')
4401
		echo '
4402
					content.write(\'<hr />\n\t\t\t\');
4403
					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\');
4404
					content.write(\'<tt># chmod a+w ', implode(' ', $upcontext['chmod']['files']), '</tt>\n\t\t\t\');';
4405
4406
	echo '
4407
					content.write(\'<a href="javascript:self.close();">close</a>\n\t\t</div>\n\t</body>\n</html>\');
4408
					content.close();
4409
				}
4410
		</script>';
4411
4412
	if (!empty($upcontext['chmod']['ftp_error']))
4413
		echo '
4414
			<div class="error_message">
4415
				<div style="color: red;">
4416
					The following error was encountered when trying to connect:<br>
4417
					<br>
4418
					<code>', $upcontext['chmod']['ftp_error'], '</code>
4419
				</div>
4420
			</div>
4421
			<br>';
4422
4423
	if (empty($upcontext['chmod_in_form']))
4424
		echo '
4425
	<form action="', $upcontext['form_url'], '" method="post">';
4426
4427
	echo '
4428
		<table width="520" border="0" align="center" style="margin-bottom: 1ex;">
4429
			<tr>
4430
				<td width="26%" valign="top" class="textbox"><label for="ftp_server">', $txt['ftp_server'], ':</label></td>
4431
				<td>
4432
					<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>
4433
					<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">
4434
					<div class="smalltext block">', $txt['ftp_server_info'], '</div>
4435
				</td>
4436
			</tr><tr>
4437
				<td width="26%" valign="top" class="textbox"><label for="ftp_username">', $txt['ftp_username'], ':</label></td>
4438
				<td>
4439
					<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">
4440
					<div class="smalltext block">', $txt['ftp_username_info'], '</div>
4441
				</td>
4442
			</tr><tr>
4443
				<td width="26%" valign="top" class="textbox"><label for="ftp_password">', $txt['ftp_password'], ':</label></td>
4444
				<td>
4445
					<input type="password" size="50" name="ftp_password" id="ftp_password" style="width: 99%;" class="input_password">
4446
					<div class="smalltext block">', $txt['ftp_password_info'], '</div>
4447
				</td>
4448
			</tr><tr>
4449
				<td width="26%" valign="top" class="textbox"><label for="ftp_path">', $txt['ftp_path'], ':</label></td>
4450
				<td style="padding-bottom: 1ex;">
4451
					<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">
4452
					<div class="smalltext block">', !empty($upcontext['chmod']['path']) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'], '</div>
4453
				</td>
4454
			</tr>
4455
		</table>
4456
4457
		<div class="righttext" style="margin: 1ex;"><input type="submit" value="', $txt['ftp_connect'], '" class="button_submit"></div>
4458
	</div>';
4459
4460
	if (empty($upcontext['chmod_in_form']))
4461
		echo '
4462
	</form>';
4463
}
4464
4465
function template_upgrade_above()
4466
{
4467
	global $modSettings, $txt, $settings, $upcontext, $upgradeurl;
4468
4469
	echo '<!DOCTYPE html>
4470
<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>
4471
	<head>
4472
		<meta charset="', isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8', '">
4473
		<meta name="robots" content="noindex">
4474
		<title>', $txt['upgrade_upgrade_utility'], '</title>
4475
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/index.css?alp21">
4476
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/install.css?alp21">
4477
		', $txt['lang_rtl'] == true ? '<link rel="stylesheet" href="' . $settings['default_theme_url'] . '/css/rtl.css?alp21">' : '' , '
4478
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
4479
		<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
4480
		<script>
4481
			var smf_scripturl = \'', $upgradeurl, '\';
4482
			var smf_charset = \'', (empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'UTF-8' : $txt['lang_character_set']) : $modSettings['global_character_set']), '\';
4483
			var startPercent = ', $upcontext['overall_percent'], ';
4484
4485
			// This function dynamically updates the step progress bar - and overall one as required.
4486
			function updateStepProgress(current, max, overall_weight)
4487
			{
4488
				// What out the actual percent.
4489
				var width = parseInt((current / max) * 100);
4490
				if (document.getElementById(\'step_progress_upgrade\'))
4491
				{
4492
					document.getElementById(\'step_progress_upgrade\').style.width = width + "%";
4493
					setInnerHTML(document.getElementById(\'step_text_upgrade\'), width + "%");
4494
				}
4495
				if (overall_weight && document.getElementById(\'overall_progress_upgrade\'))
4496
				{
4497
					overall_width = parseInt(startPercent + width * (overall_weight / 100));
4498
					document.getElementById(\'overall_progress_upgrade\').style.width = overall_width + "%";
4499
					setInnerHTML(document.getElementById(\'overall_text_upgrade\'), overall_width + "%");
4500
				}
4501
			}
4502
		</script>
4503
	</head>
4504
	<body>
4505
	<div id="footerfix">
4506
		<div id="header">
4507
			<h1 class="forumtitle">', $txt['upgrade_upgrade_utility'], '</h1>
4508
			<img id="smflogo" src="', $settings['default_theme_url'], '/images/smflogo.png" alt="Simple Machines Forum" title="Simple Machines Forum">
4509
		</div>
4510
	<div id="wrapper">
4511
	<div id="upper_section">
4512
		<div id="main_content_section">
4513
			<div id="main_steps">
4514
				<h2>', $txt['upgrade_progress'], '</h2>
4515
				<ul>';
4516
4517 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...
4518
		echo '
4519
						<li class="', $num < $upcontext['current_step'] ? 'stepdone' : ($num == $upcontext['current_step'] ? 'stepcurrent' : 'stepwaiting'), '">', $txt['upgrade_step'], ' ', $step[0], ': ', $step[1], '</li>';
4520
4521
	echo '
4522
					</ul>
4523
			</div>
4524
4525
			<div id="progress">
4526
				<div id="overall_text_upgrade">', $upcontext['overall_percent'], '%</div>
4527
				<div id="overall_progress_upgrade" style="width: ', $upcontext['overall_percent'], '%;">&nbsp;</div>
4528
				<div class="over_progress">', $txt['upgrade_overall_progress'], '</div>
4529
			</div>';
4530
4531
	if (isset($upcontext['step_progress']))
4532
		echo '
4533
				<br>
4534
				<br>
4535
				<div id="progress">
4536
					<div id="step_text_upgrade">', $upcontext['step_progress'], '%</div>
4537
					<div id="step_progress_upgrade" style="width: ', $upcontext['step_progress'], '%;background-color: #ffd000;">&nbsp;</div>
4538
					<div class="over_progress">', $txt['upgrade_step_progress'], '</div>
4539
					</div>';
4540
4541
	echo '
4542
				<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>
4543
				<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', ';">
4544
					<div id="substep_text" style="color: #000; position: absolute; margin-left: -5em;">', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : '', '%</div>
4545
				<div id="substep_progress" style="width: ', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%; height: 12pt; z-index: 1; background-color: #eebaf4;">&nbsp;</div>
4546
								</div>';
4547
4548
	// How long have we been running this?
4549
	$elapsed = time() - $upcontext['started'];
4550
	$mins = (int) ($elapsed / 60);
4551
	$seconds = $elapsed - $mins * 60;
4552
	echo '
4553
								<br> <br> <br> <br> <br>
4554
								<div class="smalltext" style="padding: 5px; text-align: center;"><br>', $txt['upgrade_time_elapsed'], ':
4555
									<span id="mins_elapsed">', $mins, '</span> ', $txt['upgrade_time_mins'], ', <span id="secs_elapsed">', $seconds, '</span> ', $txt['upgrade_time_secs'], '.
4556
								</div>';
4557
	echo '
4558
			</div>
4559
			</div>
4560
			<div id="content_section">
4561
			<div id="main_screen" class="clear">
4562
				<h2>', $upcontext['page_title'], '</h2>
4563
				<div class="panel">
4564
					<div style="max-height: 360px; overflow: auto;">';
4565
}
4566
4567
function template_upgrade_below()
4568
{
4569
	global $upcontext, $txt;
4570
4571
	if (!empty($upcontext['pause']))
4572
		echo '
4573
								<em>', $txt['upgrade_incomplete'], '.</em><br>
4574
4575
								<h2 style="margin-top: 2ex;">', $txt['upgrade_not_quite_done'], '</h2>
4576
								<h3>
4577
									', $txt['upgrade_paused_overload'], '
4578
								</h3>';
4579
4580
	if (!empty($upcontext['custom_warning']))
4581
		echo '
4582
								<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
4583
									<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4584
									<strong style="text-decoration: underline;">', $txt['upgrade_note'], '</strong><br>
4585
									<div style="padding-left: 6ex;">', $upcontext['custom_warning'], '</div>
4586
								</div>';
4587
4588
	echo '
4589
								<div class="righttext" style="margin: 1ex;">';
4590
4591
	if (!empty($upcontext['continue']))
4592
		echo '
4593
									<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '"', $upcontext['continue'] == 2 ? ' disabled' : '', ' class="button_submit">';
4594
	if (!empty($upcontext['skip']))
4595
		echo '
4596
									<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="dontSubmit = true; document.getElementById(\'contbutt\').disabled = \'disabled\'; return true;" class="button_submit">';
4597
4598
	echo '
4599
								</div>
4600
							</form>
4601
						</div>
4602
				</div>
4603
			</div>
4604
			</div>
4605
		</div>
4606
		</div>
4607
		<div id="footer">
4608
			<ul>
4609
				<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>
4610
			</ul>
4611
		</div>
4612
	</body>
4613
</html>';
4614
4615
	// Are we on a pause?
4616
	if (!empty($upcontext['pause']))
4617
	{
4618
		echo '
4619
		<script>
4620
			window.onload = doAutoSubmit;
4621
			var countdown = 3;
4622
			var dontSubmit = false;
4623
4624
			function doAutoSubmit()
4625
			{
4626
				if (countdown == 0 && !dontSubmit)
4627
					document.upform.submit();
4628
				else if (countdown == -1)
4629
					return;
4630
4631
				document.getElementById(\'contbutt\').value = "', $txt['upgrade_continue'], ' (" + countdown + ")";
4632
				countdown--;
4633
4634
				setTimeout("doAutoSubmit();", 1000);
4635
			}
4636
		</script>';
4637
	}
4638
}
4639
4640
function template_xml_above()
4641
{
4642
	global $upcontext;
4643
4644
	echo '<', '?xml version="1.0" encoding="UTF-8"?', '>
4645
	<smf>';
4646
4647
	if (!empty($upcontext['get_data']))
4648
		foreach ($upcontext['get_data'] as $k => $v)
4649
			echo '
4650
		<get key="', $k, '">', $v, '</get>';
4651
}
4652
4653
function template_xml_below()
4654
{
4655
	echo '
4656
		</smf>';
4657
}
4658
4659
function template_error_message()
4660
{
4661
	global $upcontext;
4662
4663
	echo '
4664
	<div class="error_message">
4665
		<div style="color: red;">
4666
			', $upcontext['error_msg'], '
4667
		</div>
4668
		<br>
4669
		<a href="', $_SERVER['PHP_SELF'], '">Click here to try again.</a>
4670
	</div>';
4671
}
4672
4673
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...
4674
{
4675
	global $upcontext, $disable_security, $settings, $txt;
4676
4677
	echo '
4678
		<script src="http://www.simplemachines.org/smf/current-version.js?version=' . SMF_VERSION . '"></script>
4679
			<h3>', sprintf($txt['upgrade_ready_proceed'], SMF_VERSION), '</h3>
4680
	<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">
4681
		<input type="hidden" name="', $upcontext['login_token_var'], '" value="', $upcontext['login_token'], '">
4682
		<div id="version_warning" style="margin: 2ex; padding: 2ex; border: 2px dashed #a92174; color: black; background-color: #fbbbe2; display: none;">
4683
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4684
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
4685
			<div style="padding-left: 6ex;">
4686
				', sprintf($txt['upgrade_warning_out_of_date'], SMF_VERSION), '
4687
			</div>
4688
		</div>';
4689
4690
	$upcontext['chmod_in_form'] = true;
4691
	template_chmod();
4692
4693
	// For large, pre 1.1 RC2 forums give them a warning about the possible impact of this upgrade!
4694
	if ($upcontext['is_large_forum'])
4695
		echo '
4696
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
4697
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4698
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
4699
			<div style="padding-left: 6ex;">
4700
				', $txt['upgrade_warning_lots_data'], '
4701
			</div>
4702
		</div>';
4703
4704
	// A warning message?
4705
	if (!empty($upcontext['warning']))
4706
		echo '
4707
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
4708
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4709
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
4710
			<div style="padding-left: 6ex;">
4711
				', $upcontext['warning'], '
4712
			</div>
4713
		</div>';
4714
4715
	// Paths are incorrect?
4716
	echo '
4717
		<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">
4718
			<div style="float: left; width: 2ex; font-size: 2em; color: black;">!!</div>
4719
			<strong style="text-decoration: underline;">', $txt['upgrade_critical_error'], '</strong><br>
4720
			<div style="padding-left: 6ex;">
4721
				', $txt['upgrade_error_script_js'], '
4722
			</div>
4723
		</div>';
4724
4725
	// Is there someone already doing this?
4726
	if (!empty($upcontext['user']['id']) && (time() - $upcontext['started'] < 72600 || time() - $upcontext['updated'] < 3600))
4727
	{
4728
		$ago = time() - $upcontext['started'];
4729
		if ($ago < 60)
4730
			$ago = $ago . ' seconds';
4731
		elseif ($ago < 3600)
4732
			$ago = (int) ($ago / 60) . ' minutes';
4733
		else
4734
			$ago = (int) ($ago / 3600) . ' hours';
4735
4736
		$active = time() - $upcontext['updated'];
4737
		if ($active < 60)
4738
			$updated = $active . ' seconds';
4739
		elseif ($active < 3600)
4740
			$updated = (int) ($active / 60) . ' minutes';
4741
		else
4742
			$updated = (int) ($active / 3600) . ' hours';
4743
4744
		echo '
4745
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
4746
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4747
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
4748
			<div style="padding-left: 6ex;">
4749
				&quot;', $upcontext['user']['name'], '&quot; has been running the upgrade script for the last ', $ago, ' - and was last active ', $updated, ' ago.';
4750
4751
		if ($active < 600)
4752
			echo '
4753
				We recommend that you do not run this script unless you are sure that ', $upcontext['user']['name'], ' has completed their upgrade.';
4754
4755
		if ($active > $upcontext['inactive_timeout'])
4756
			echo '
4757
				<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.';
4758
		else
4759
			echo '
4760
				<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!');
4761
4762
		echo '
4763
			</div>
4764
		</div>';
4765
	}
4766
4767
	echo '
4768
			<strong>Admin Login: ', $disable_security ? '(DISABLED)' : '', '</strong>
4769
			<h3>For security purposes please login with your admin account to proceed with the upgrade.</h3>
4770
			<table>
4771
				<tr valign="top">
4772
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Username:</strong></td>
4773
					<td>
4774
						<input type="text" name="user" value="', !empty($upcontext['username']) ? $upcontext['username'] : '', '"', $disable_security ? ' disabled' : '', ' class="input_text">';
4775
4776
	if (!empty($upcontext['username_incorrect']))
4777
		echo '
4778
						<div class="smalltext" style="color: red;">Username Incorrect</div>';
4779
4780
	echo '
4781
					</td>
4782
				</tr>
4783
				<tr valign="top">
4784
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Password:</strong></td>
4785
					<td>
4786
						<input type="password" name="passwrd" value=""', $disable_security ? ' disabled' : '', ' class="input_password">
4787
						<input type="hidden" name="hash_passwrd" value="">';
4788
4789
	if (!empty($upcontext['password_failed']))
4790
		echo '
4791
						<div class="smalltext" style="color: red;">Password Incorrect</div>';
4792
4793
	echo '
4794
					</td>
4795
				</tr>';
4796
4797
	// Can they continue?
4798
	if (!empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] >= $upcontext['inactive_timeout'] && $upcontext['user']['step'] > 1)
4799
	{
4800
		echo '
4801
				<tr>
4802
					<td colspan="2">
4803
						<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>
4804
					</td>
4805
				</tr>';
4806
	}
4807
4808
	echo '
4809
			</table><br>
4810
			<span class="smalltext">
4811
				<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.
4812
			</span>
4813
			<input type="hidden" name="login_attempt" id="login_attempt" value="1">
4814
			<input type="hidden" name="js_works" id="js_works" value="0">';
4815
4816
	// Say we want the continue button!
4817
	$upcontext['continue'] = !empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] < $upcontext['inactive_timeout'] ? 2 : 1;
4818
4819
	// This defines whether javascript is going to work elsewhere :D
4820
	echo '
4821
		<script>
4822
			if (\'XMLHttpRequest\' in window && document.getElementById(\'js_works\'))
4823
				document.getElementById(\'js_works\').value = 1;
4824
4825
			// Latest version?
4826
			function smfCurrentVersion()
4827
			{
4828
				var smfVer, yourVer;
4829
4830
				if (!(\'smfVersion\' in window))
4831
					return;
4832
4833
				window.smfVersion = window.smfVersion.replace(/SMF\s?/g, \'\');
4834
4835
				smfVer = document.getElementById(\'smfVersion\');
4836
				yourVer = document.getElementById(\'yourVersion\');
4837
4838
				setInnerHTML(smfVer, window.smfVersion);
4839
4840
				var currentVersion = getInnerHTML(yourVer);
4841
				if (currentVersion < window.smfVersion)
4842
					document.getElementById(\'version_warning\').style.display = \'\';
4843
			}
4844
			addLoadEvent(smfCurrentVersion);
4845
4846
			// This checks that the script file even exists!
4847
			if (typeof(smfSelectText) == \'undefined\')
4848
				document.getElementById(\'js_script_missing_error\').style.display = \'\';
4849
4850
		</script>';
4851
}
4852
4853
function template_upgrade_options()
4854
{
4855
	global $upcontext, $modSettings, $db_prefix, $mmessage, $mtitle, $db_type;
4856
4857
	echo '
4858
			<h3>Before the upgrade gets underway please review the options below - and hit continue when you\'re ready to begin.</h3>
4859
			<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">';
4860
4861
	// Warning message?
4862
	if (!empty($upcontext['upgrade_options_warning']))
4863
		echo '
4864
		<div style="margin: 1ex; padding: 1ex; border: 1px dashed #cc3344; color: black; background-color: #ffe4e9;">
4865
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
4866
			<strong style="text-decoration: underline;">Warning!</strong><br>
4867
			<div style="padding-left: 4ex;">
4868
				', $upcontext['upgrade_options_warning'], '
4869
			</div>
4870
		</div>';
4871
4872
	echo '
4873
				<table>
4874
					<tr valign="top">
4875
						<td width="2%">
4876
							<input type="checkbox" name="backup" id="backup" value="1"', $db_type != 'mysql' && $db_type != 'mysqli' && $db_type != 'postgresql' ? ' disabled' : '', ' class="input_check">
4877
						</td>
4878
						<td width="100%">
4879
							<label for="backup">Backup tables in your database with the prefix &quot;backup_' . $db_prefix . '&quot;.</label>', isset($modSettings['smfVersion']) ? '' : ' (recommended!)', '
4880
						</td>
4881
					</tr>
4882
					<tr valign="top">
4883
						<td width="2%">
4884
							<input type="checkbox" name="maint" id="maint" value="1" checked class="input_check">
4885
						</td>
4886
						<td width="100%">
4887
							<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>
4888
							<div id="mainmess" style="display: none;">
4889
								<strong class="smalltext">Maintenance Title: </strong><br>
4890
								<input type="text" name="maintitle" size="30" value="', htmlspecialchars($mtitle), '" class="input_text"><br>
4891
								<strong class="smalltext">Maintenance Message: </strong><br>
4892
								<textarea name="mainmessage" rows="3" cols="50">', htmlspecialchars($mmessage), '</textarea>
4893
							</div>
4894
						</td>
4895
					</tr>';
4896
4897
	// Offer mysql users to switch to mysqli
4898
	if ($db_type == 'mysql' && function_exists('mysqli_query'))
4899
		echo '
4900
					<tr valign="top">
4901
						<td width="2%">
4902
							<input type="checkbox" name="convertMysql" id="convertMysql" value="1" checked class="input_check">
4903
						</td>
4904
						<td width="100%">
4905
							<label for="convertMysql">Use MySQLi functionality (MySQL compatible).</span>
4906
							<strong class="smalltext"><a href="http://wiki.simplemachines.org/smf/Upgrading-MySQLi-Functionality" target="_blank">More information about MySQLi</a></strong><br>
4907
						</td>
4908
					</tr>';
4909
4910
	echo '
4911
					<tr valign="top">
4912
						<td width="2%">
4913
							<input type="checkbox" name="debug" id="debug" value="1" class="input_check">
4914
						</td>
4915
						<td width="100%">
4916
							<label for="debug">Output extra debugging information</label>
4917
						</td>
4918
					</tr>
4919
					<tr valign="top">
4920
						<td width="2%">
4921
							<input type="checkbox" name="empty_error" id="empty_error" value="1" class="input_check">
4922
						</td>
4923
						<td width="100%">
4924
							<label for="empty_error">Empty error log before upgrading</label>
4925
						</td>
4926
					</tr>';
4927
4928
	if (!empty($upcontext['karma_installed']['good']) || !empty($upcontext['karma_installed']['bad']))
4929
		echo '
4930
					<tr valign="top">
4931
						<td width="2%">
4932
							<input type="checkbox" name="delete_karma" id="delete_karma" value="1" class="input_check">
4933
						</td>
4934
						<td width="100%">
4935
							<label for="delete_karma">Delete all karma settings and info from the DB</label>
4936
						</td>
4937
					</tr>';
4938
4939
	echo '
4940
					<tr valign="top">
4941
						<td width="2%">
4942
							<input type="checkbox" name="stat" id="stat" value="1"', empty($modSettings['allow_sm_stats']) ? '' : ' checked', ' class="input_check">
4943
						</td>
4944
						<td width="100%">
4945
							<label for="stat">
4946
								Allow Simple Machines to Collect Basic Stats Monthly.<br>
4947
								<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>
4948
							</label>
4949
						</td>
4950
					</tr>
4951
				</table>
4952
				<input type="hidden" name="upcont" value="1">';
4953
4954
	// We need a normal continue button here!
4955
	$upcontext['continue'] = 1;
4956
}
4957
4958
// Template for the database backup tool/
4959 View Code Duplication
function template_backup_database()
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...
4960
{
4961
	global $upcontext, $support_js, $is_debug;
4962
4963
	echo '
4964
			<h3>Please wait while a backup is created. For large forums this may take some time!</h3>';
4965
4966
	echo '
4967
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4968
			<input type="hidden" name="backup_done" id="backup_done" value="0">
4969
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
4970
			<span id="debuginfo"></span>';
4971
4972
	// Dont any tables so far?
4973
	if (!empty($upcontext['previous_tables']))
4974
		foreach ($upcontext['previous_tables'] as $table)
4975
			echo '
4976
			<br>Completed Table: &quot;', $table, '&quot;.';
4977
4978
	echo '
4979
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
4980
			<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>';
4981
4982
	// Continue please!
4983
	$upcontext['continue'] = $support_js ? 2 : 1;
4984
4985
	// If javascript allows we want to do this using XML.
4986
	if ($support_js)
4987
	{
4988
		echo '
4989
		<script>
4990
			var lastTable = ', $upcontext['cur_table_num'], ';
4991
			function getNextTables()
4992
			{
4993
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4994
			}
4995
4996
			// Got an update!
4997
			function onBackupUpdate(oXMLDoc)
4998
			{
4999
				var sCurrentTableName = "";
5000
				var iTableNum = 0;
5001
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
5002
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
5003
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
5004
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
5005
5006
				// Update the page.
5007
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
5008
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
5009
				lastTable = iTableNum;
5010
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
5011
5012
		// If debug flood the screen.
5013
		if ($is_debug)
5014
			echo '
5015
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
5016
5017
				if (document.getElementById(\'debuginfo\').scrollHeight)
5018
					document.getElementById(\'debuginfo\').scrollTop = document.getElementById(\'debuginfo\').scrollHeight;';
5019
5020
		echo '
5021
				// Get the next update...
5022
				if (iTableNum == ', $upcontext['table_count'], ')
5023
				{
5024
					document.getElementById(\'commess\').style.display = "";
5025
					document.getElementById(\'current_tab_div\').style.display = "none";
5026
					document.getElementById(\'contbutt\').disabled = 0;
5027
					document.getElementById(\'backup_done\').value = 1;
5028
				}
5029
				else
5030
					getNextTables();
5031
			}
5032
			getNextTables();
5033
		</script>';
5034
	}
5035
}
5036
5037
function template_backup_xml()
5038
{
5039
	global $upcontext;
5040
5041
	echo '
5042
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
5043
}
5044
5045
// Here is the actual "make the changes" template!
5046
function template_database_changes()
5047
{
5048
	global $upcontext, $support_js, $is_debug, $timeLimitThreshold;
5049
5050
	echo '
5051
		<h3>Executing database changes</h3>
5052
		<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>';
5053
5054
	echo '
5055
		<form action="', $upcontext['form_url'], '&amp;filecount=', $upcontext['file_count'], '" name="upform" id="upform" method="post">
5056
		<input type="hidden" name="database_done" id="database_done" value="0">';
5057
5058
	// No javascript looks rubbish!
5059
	if (!$support_js)
5060
	{
5061
		foreach ($upcontext['actioned_items'] as $num => $item)
5062
		{
5063
			if ($num != 0)
5064
				echo ' Successful!';
5065
			echo '<br>' . $item;
5066
		}
5067
		if (!empty($upcontext['changes_complete']))
5068
			echo ' Successful!<br><br><span id="commess" style="font-weight: bold;">Database Updates Complete! Click Continue to Proceed.</span><br>';
5069
	}
5070
	else
5071
	{
5072
		// Tell them how many files we have in total.
5073
		if ($upcontext['file_count'] > 1)
5074
			echo '
5075
		<strong id="info1">Executing upgrade script <span id="file_done">', $upcontext['cur_file_num'], '</span> of ', $upcontext['file_count'], '.</strong>';
5076
5077
		echo '
5078
		<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>
5079
		<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>';
5080
5081
		if ($is_debug)
5082
		{
5083
			echo '
5084
			<div id="debug_section" style="height: 200px; overflow: auto;">
5085
			<span id="debuginfo"></span>
5086
			</div>';
5087
		}
5088
	}
5089
5090
	// Place for the XML error message.
5091
	echo '
5092
		<div id="error_block" style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9; display: ', empty($upcontext['error_message']) ? 'none' : '', ';">
5093
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
5094
			<strong style="text-decoration: underline;">Error!</strong><br>
5095
			<div style="padding-left: 6ex;" id="error_message">', isset($upcontext['error_message']) ? $upcontext['error_message'] : 'Unknown Error!', '</div>
5096
		</div>';
5097
5098
	// We want to continue at some point!
5099
	$upcontext['continue'] = $support_js ? 2 : 1;
5100
5101
	// If javascript allows we want to do this using XML.
5102
	if ($support_js)
5103
	{
5104
		echo '
5105
		<script>
5106
			var lastItem = ', $upcontext['current_debug_item_num'], ';
5107
			var sLastString = "', strtr($upcontext['current_debug_item_name'], array('"' => '&quot;')), '";
5108
			var iLastSubStepProgress = -1;
5109
			var curFile = ', $upcontext['cur_file_num'], ';
5110
			var totalItems = 0;
5111
			var prevFile = 0;
5112
			var retryCount = 0;
5113
			var testvar = 0;
5114
			var timeOutID = 0;
5115
			var getData = "";
5116
			var debugItems = ', $upcontext['debug_items'], ';
5117
			function getNextItem()
5118
			{
5119
				// We want to track this...
5120
				if (timeOutID)
5121
					clearTimeout(timeOutID);
5122
				timeOutID = window.setTimeout("retTimeout()", ', (10 * $timeLimitThreshold), '000);
5123
5124
				getXMLDocument(\'', $upcontext['form_url'], '&xml&filecount=', $upcontext['file_count'], '&substep=\' + lastItem + getData, onItemUpdate);
5125
			}
5126
5127
			// Got an update!
5128
			function onItemUpdate(oXMLDoc)
5129
			{
5130
				var sItemName = "";
5131
				var sDebugName = "";
5132
				var iItemNum = 0;
5133
				var iSubStepProgress = -1;
5134
				var iDebugNum = 0;
5135
				var bIsComplete = 0;
5136
				getData = "";
5137
5138
				// We\'ve got something - so reset the timeout!
5139
				if (timeOutID)
5140
					clearTimeout(timeOutID);
5141
5142
				// Assume no error at this time...
5143
				document.getElementById("error_block").style.display = "none";
5144
5145
				// Are we getting some duff info?
5146
				if (!oXMLDoc.getElementsByTagName("item")[0])
5147
				{
5148
					// Too many errors?
5149
					if (retryCount > 15)
5150
					{
5151
						document.getElementById("error_block").style.display = "";
5152
						setInnerHTML(document.getElementById("error_message"), "Error retrieving information on step: " + (sDebugName == "" ? sLastString : sDebugName));';
5153
5154
	if ($is_debug)
5155
		echo '
5156
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
5157
5158
	echo '
5159
					}
5160
					else
5161
					{
5162
						retryCount++;
5163
						getNextItem();
5164
					}
5165
					return false;
5166
				}
5167
5168
				// Never allow loops.
5169
				if (curFile == prevFile)
5170
				{
5171
					retryCount++;
5172
					if (retryCount > 10)
5173
					{
5174
						document.getElementById("error_block").style.display = "";
5175
						setInnerHTML(document.getElementById("error_message"), "Upgrade script appears to be going into a loop - step: " + sDebugName);';
5176
5177
	if ($is_debug)
5178
		echo '
5179
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
5180
5181
	echo '
5182
					}
5183
				}
5184
				retryCount = 0;
5185
5186
				for (var i = 0; i < oXMLDoc.getElementsByTagName("item")[0].childNodes.length; i++)
5187
					sItemName += oXMLDoc.getElementsByTagName("item")[0].childNodes[i].nodeValue;
5188
				for (var i = 0; i < oXMLDoc.getElementsByTagName("debug")[0].childNodes.length; i++)
5189
					sDebugName += oXMLDoc.getElementsByTagName("debug")[0].childNodes[i].nodeValue;
5190
				for (var i = 0; i < oXMLDoc.getElementsByTagName("get").length; i++)
5191
				{
5192
					getData += "&" + oXMLDoc.getElementsByTagName("get")[i].getAttribute("key") + "=";
5193
					for (var j = 0; j < oXMLDoc.getElementsByTagName("get")[i].childNodes.length; j++)
5194
					{
5195
						getData += oXMLDoc.getElementsByTagName("get")[i].childNodes[j].nodeValue;
5196
					}
5197
				}
5198
5199
				iItemNum = oXMLDoc.getElementsByTagName("item")[0].getAttribute("num");
5200
				iDebugNum = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("num"));
5201
				bIsComplete = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("complete"));
5202
				iSubStepProgress = parseFloat(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("percent"));
5203
				sLastString = sDebugName + " (Item: " + iDebugNum + ")";
5204
5205
				curFile = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("num"));
5206
				debugItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("debug_items"));
5207
				totalItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("items"));
5208
5209
				// If we have an error we haven\'t completed!
5210
				if (oXMLDoc.getElementsByTagName("error")[0] && bIsComplete)
5211
					iDebugNum = lastItem;
5212
5213
				// Do we have the additional progress bar?
5214
				if (iSubStepProgress != -1)
5215
				{
5216
					document.getElementById("substep_bar_div").style.display = "";
5217
					document.getElementById("substep_bar_div2").style.display = "";
5218
					document.getElementById("substep_progress").style.width = iSubStepProgress + "%";
5219
					setInnerHTML(document.getElementById("substep_text"), iSubStepProgress + "%");
5220
					setInnerHTML(document.getElementById("substep_bar_div"), sDebugName.replace(/\./g, "") + ":");
5221
				}
5222
				else
5223
				{
5224
					document.getElementById("substep_bar_div").style.display = "none";
5225
					document.getElementById("substep_bar_div2").style.display = "none";
5226
				}
5227
5228
				// Move onto the next item?
5229
				if (bIsComplete)
5230
					lastItem = iDebugNum;
5231
				else
5232
					lastItem = iDebugNum - 1;
5233
5234
				// Are we finished?
5235
				if (bIsComplete && iDebugNum == -1 && curFile >= ', $upcontext['file_count'], ')
5236
				{';
5237
5238
		if ($is_debug)
5239
			echo '
5240
					document.getElementById(\'debug_section\').style.display = "none";';
5241
5242
		echo '
5243
5244
					document.getElementById(\'commess\').style.display = "";
5245
					document.getElementById(\'contbutt\').disabled = 0;
5246
					document.getElementById(\'database_done\').value = 1;';
5247
5248
		if ($upcontext['file_count'] > 1)
5249
			echo '
5250
					document.getElementById(\'info1\').style.display = "none";';
5251
5252
		echo '
5253
					document.getElementById(\'info2\').style.display = "none";
5254
					updateStepProgress(100, 100, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
5255
					return true;
5256
				}
5257
				// Was it the last step in the file?
5258
				else if (bIsComplete && iDebugNum == -1)
5259
				{
5260
					lastItem = 0;
5261
					prevFile = curFile;';
5262
5263
		if ($is_debug)
5264
			echo '
5265
					setOuterHTML(document.getElementById(\'debuginfo\'), \'Moving to next script file...done<br><span id="debuginfo"><\' + \'/span>\');';
5266
5267
		echo '
5268
					getNextItem();
5269
					return true;
5270
				}';
5271
5272
		// If debug scroll the screen.
5273
		if ($is_debug)
5274
			echo '
5275
				if (iLastSubStepProgress == -1)
5276
				{
5277
					// Give it consistent dots.
5278
					dots = sDebugName.match(/\./g);
5279
					numDots = dots ? dots.length : 0;
5280
					for (var i = numDots; i < 3; i++)
5281
						sDebugName += ".";
5282
					setOuterHTML(document.getElementById(\'debuginfo\'), sDebugName + \'<span id="debuginfo"><\' + \'/span>\');
5283
				}
5284
				iLastSubStepProgress = iSubStepProgress;
5285
5286
				if (bIsComplete)
5287
					setOuterHTML(document.getElementById(\'debuginfo\'), \'done<br><span id="debuginfo"><\' + \'/span>\');
5288
				else
5289
					setOuterHTML(document.getElementById(\'debuginfo\'), \'...<span id="debuginfo"><\' + \'/span>\');
5290
5291
				if (document.getElementById(\'debug_section\').scrollHeight)
5292
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
5293
5294
		echo '
5295
				// Update the page.
5296
				setInnerHTML(document.getElementById(\'item_num\'), iItemNum);
5297
				setInnerHTML(document.getElementById(\'cur_item_name\'), sItemName);';
5298
5299
		if ($upcontext['file_count'] > 1)
5300
		{
5301
			echo '
5302
				setInnerHTML(document.getElementById(\'file_done\'), curFile);
5303
				setInnerHTML(document.getElementById(\'item_count\'), totalItems);';
5304
		}
5305
5306
		echo '
5307
				// Is there an error?
5308
				if (oXMLDoc.getElementsByTagName("error")[0])
5309
				{
5310
					var sErrorMsg = "";
5311
					for (var i = 0; i < oXMLDoc.getElementsByTagName("error")[0].childNodes.length; i++)
5312
						sErrorMsg += oXMLDoc.getElementsByTagName("error")[0].childNodes[i].nodeValue;
5313
					document.getElementById("error_block").style.display = "";
5314
					setInnerHTML(document.getElementById("error_message"), sErrorMsg);
5315
					return false;
5316
				}
5317
5318
				// Get the progress bar right.
5319
				barTotal = debugItems * ', $upcontext['file_count'], ';
5320
				barDone = (debugItems * (curFile - 1)) + lastItem;
5321
5322
				updateStepProgress(barDone, barTotal, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
5323
5324
				// Finally - update the time here as it shows the server is responding!
5325
				curTime = new Date();
5326
				iElapsed = (curTime.getTime() / 1000 - ', $upcontext['started'], ');
5327
				mins = parseInt(iElapsed / 60);
5328
				secs = parseInt(iElapsed - mins * 60);
5329
				setInnerHTML(document.getElementById("mins_elapsed"), mins);
5330
				setInnerHTML(document.getElementById("secs_elapsed"), secs);
5331
5332
				getNextItem();
5333
				return true;
5334
			}
5335
5336
			// What if we timeout?!
5337
			function retTimeout(attemptAgain)
5338
			{
5339
				// Oh noes...
5340
				if (!attemptAgain)
5341
				{
5342
					document.getElementById("error_block").style.display = "";
5343
					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");
5344
				}
5345
				else
5346
				{
5347
					document.getElementById("error_block").style.display = "none";
5348
					getNextItem();
5349
				}
5350
			}';
5351
5352
		// Start things off assuming we've not errored.
5353
		if (empty($upcontext['error_message']))
5354
			echo '
5355
			getNextItem();';
5356
5357
		echo '
5358
		</script>';
5359
	}
5360
	return;
5361
}
5362
5363
function template_database_xml()
5364
{
5365
	global $upcontext, $txt;
5366
5367
	echo '
5368
	<file num="', $upcontext['cur_file_num'], '" items="', $upcontext['total_items'], '" debug_items="', $upcontext['debug_items'], '">', $upcontext['cur_file_name'], '</file>
5369
	<item num="', $upcontext['current_item_num'], '">', $upcontext['current_item_name'], '</item>
5370
	<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>';
5371
5372
	if (!empty($upcontext['error_message']))
5373
		echo '
5374
	<error>', $upcontext['error_message'], '</error>';
5375
}
5376
5377
// Template for the UTF-8 conversion step. Basically a copy of the backup stuff with slight modifications....
5378
function template_convert_utf8()
5379
{
5380
	global $upcontext, $support_js, $is_debug;
5381
5382
	echo '
5383
			<h3>Please wait while your database is converted to UTF-8. For large forums this may take some time!</h3>';
5384
5385
	echo '
5386
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
5387
			<input type="hidden" name="utf8_done" id="utf8_done" value="0">
5388
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
5389
			<span id="debuginfo"></span>';
5390
5391
	// Done any tables so far?
5392
	if (!empty($upcontext['previous_tables']))
5393
		foreach ($upcontext['previous_tables'] as $table)
5394
			echo '
5395
			<br>Completed Table: &quot;', $table, '&quot;.';
5396
5397
	echo '
5398
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>';
5399
5400
	// If we dropped their index, let's let them know
5401
	if ($upcontext['cur_table_num'] == $upcontext['table_count'] && $upcontext['dropping_index'])
5402
		echo '
5403
			<br><span style="display:inline;">Please note that your fulltext index was dropped to facilitate the conversion and will need to be recreated.</span>';
5404
5405
	echo '
5406
			<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>';
5407
5408
	// Continue please!
5409
	$upcontext['continue'] = $support_js ? 2 : 1;
5410
5411
	// If javascript allows we want to do this using XML.
5412
	if ($support_js)
5413
	{
5414
		echo '
5415
		<script>
5416
			var lastTable = ', $upcontext['cur_table_num'], ';
5417
			function getNextTables()
5418
			{
5419
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
5420
			}
5421
5422
			// Got an update!
5423
			function onBackupUpdate(oXMLDoc)
5424
			{
5425
				var sCurrentTableName = "";
5426
				var iTableNum = 0;
5427
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
5428
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
5429
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
5430
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
5431
5432
				// Update the page.
5433
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
5434
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
5435
				lastTable = iTableNum;
5436
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
5437
5438
		// If debug flood the screen.
5439
		if ($is_debug)
5440
			echo '
5441
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');';
5442
5443
		echo '
5444
				// Get the next update...
5445
				if (iTableNum == ', $upcontext['table_count'], ')
5446
				{
5447
					document.getElementById(\'commess\').style.display = "";
5448
					document.getElementById(\'current_tab_div\').style.display = "none";
5449
					document.getElementById(\'contbutt\').disabled = 0;
5450
					document.getElementById(\'utf8_done\').value = 1;
5451
				}
5452
				else
5453
					getNextTables();
5454
			}
5455
			getNextTables();
5456
		</script>';
5457
	}
5458
}
5459
5460
function template_utf8_xml()
5461
{
5462
	global $upcontext;
5463
5464
	echo '
5465
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
5466
}
5467
5468
function template_clean_mods()
5469
{
5470
	global $upcontext;
5471
5472
	$upcontext['chmod_in_form'] = true;
5473
5474
	echo '
5475
	<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>
5476
	<form action="', $upcontext['form_url'], '&amp;ssi=1" name="upform" id="upform" method="post">';
5477
5478
	// In case it's required.
5479
	template_chmod();
5480
5481
	echo '
5482
		<table width="90%" align="center" style="background-color: black;">
5483
			<tr style="background-color: #eeeeee;">
5484
				<td width="40%"><strong>Modification Name</strong></td>
5485
				<td width="10%" align="center"><strong>Version</strong></td>
5486
				<td width="15%"><strong>Files Affected</strong></td>
5487
				<td width="20%"><strong>Status</strong></td>
5488
				<td width="5%" align="center"><strong>Fix?</strong></td>
5489
			</tr>';
5490
5491
	foreach ($upcontext['packages'] as $package)
5492
	{
5493
		echo '
5494
			<tr style="background-color: #cccccc;">
5495
				<td width="40%">', $package['name'], '</td>
5496
				<td width="10%">', $package['version'], '</td>
5497
				<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>
5498
				<td width="20%"><span style="font-weight: bold; color: ', $package['color'], '">', $package['status'], '</span></td>
5499
				<td width="5%" align="center">
5500
					<input type="hidden" name="remove[', $package['id'], ']" value="0">
5501
					<input type="checkbox" name="remove[', $package['id'], ']"', $package['color'] == 'green' ? ' disabled' : '', ' class="input_check">
5502
				</td>
5503
			</tr>';
5504
	}
5505
	echo '
5506
		</table>
5507
		<input type="hidden" name="cleandone" value="1">';
5508
5509
	// Files to make writable?
5510 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...
5511
		echo '
5512
		<input type="hidden" name="writable_files" value="', base64_encode(safe_serialize($upcontext['writable_files'])), '">';
5513
5514
	// We'll want a continue button...
5515
	if (empty($upcontext['chmod']['files']))
5516
		$upcontext['continue'] = 1;
5517
}
5518
5519
// Finished with the mods - let them know what we've done.
5520
function template_cleanup_done()
5521
{
5522
	global $upcontext;
5523
5524
	echo '
5525
	<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>
5526
	<form action="', $upcontext['form_url'], '&amp;ssi=1" name="upform" id="upform" method="post">
5527
		<table width="90%" align="center" style="background-color: black;">
5528
			<tr style="background-color: #eeeeee;">
5529
				<td width="100%"><strong>Actions Completed:</strong></td>
5530
			</tr>';
5531
5532
	foreach ($upcontext['packages'] as $package)
5533
	{
5534
		echo '
5535
			<tr style="background-color: #cccccc;">
5536
				<td>', $package['name'], '... <span style="font-weight: bold; color: ', $package['color'], ';">', $package['result'], '</span></td>
5537
			</tr>';
5538
	}
5539
	echo '
5540
		</table>
5541
		<input type="hidden" name="cleandone2" value="1">';
5542
5543
	// We'll want a continue button...
5544
	$upcontext['continue'] = 1;
5545
}
5546
5547
// Do they want to upgrade their templates?
5548
function template_upgrade_templates()
5549
{
5550
	global $upcontext;
5551
5552
	echo '
5553
	<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>
5554
	<form action="', $upcontext['form_url'], '&amp;ssi=1', $upcontext['is_test'] ? '' : ';forreal=1', '" name="upform" id="upform" method="post">';
5555
5556
	// Any files need to be writable?
5557
	$upcontext['chmod_in_form'] = true;
5558
	template_chmod();
5559
5560
	// Language/Template files need an update?
5561
	if ($upcontext['temp_progress'] == 0 && !$upcontext['is_test'] && (!empty($upcontext['languages']) || !empty($upcontext['themes'])))
5562
	{
5563
		echo '
5564
		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.
5565
		<table width="90%" align="center" style="background-color: black;">
5566
			<tr style="background-color: #eeeeee;">
5567
				<td width="80%"><strong>Area</strong></td>
5568
				<td width="20%" align="center"><strong>Changes Required</strong></td>
5569
			</tr>';
5570
5571 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...
5572
		{
5573
			echo '
5574
				<tr style="background-color: #cccccc;">
5575
					<td width="80%">
5576
						&quot;', $language['name'], '&quot; Language Pack
5577
						<div class="smalltext">(';
5578
5579
			foreach ($language['files'] as $k => $file)
5580
				echo $file['name'], $k + 1 != count($language['files']) ? ', ' : ')';
5581
5582
			echo '
5583
						</div>
5584
					</td>
5585
					<td width="20%" align="center">', $language['edit_count'] == 0 ? 1 : $language['edit_count'], '</td>
5586
				</tr>';
5587
		}
5588
5589 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...
5590
		{
5591
			echo '
5592
				<tr style="background-color: #CCCCCC;">
5593
					<td width="80%">
5594
						&quot;', $theme['name'], '&quot; Theme
5595
						<div class="smalltext">(';
5596
5597
			foreach ($theme['files'] as $k => $file)
5598
				echo $file['name'], $k + 1 != count($theme['files']) ? ', ' : ')';
5599
5600
			echo '
5601
						</div>
5602
					</td>
5603
					<td width="20%" align="center">', $theme['edit_count'] == 0 ? 1 : $theme['edit_count'], '</td>
5604
				</tr>';
5605
		}
5606
5607
		echo '
5608
		</table>';
5609
	}
5610
	else
5611
	{
5612
		$langFiles = 0;
5613
		$themeFiles = 0;
5614
		if (!empty($upcontext['languages']))
5615
			foreach ($upcontext['languages'] as $lang)
0 ignored issues
show
Bug introduced by
The expression $upcontext['languages'] of type boolean is not traversable.
Loading history...
5616
				$langFiles += count($lang['files']);
5617
		if (!empty($upcontext['themes']))
5618
			foreach ($upcontext['themes'] as $theme)
0 ignored issues
show
Bug introduced by
The expression $upcontext['themes'] of type boolean is not traversable.
Loading history...
5619
				$themeFiles += count($theme['files']);
5620
		echo sprintf('Found <strong>%d</strong> language files and <strong>%d</strong> templates requiring an update so far.', $langFiles, $themeFiles) . '<br>';
5621
5622
		// What we're currently doing?
5623
		if (!empty($upcontext['current_message']))
5624
			echo '
5625
				', $upcontext['current_message'];
5626
	}
5627
5628
	echo '
5629
		<input type="hidden" name="uptempdone" value="1">';
5630
5631 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...
5632
		echo '
5633
		<input type="hidden" name="languages" value="', base64_encode(safe_serialize($upcontext['languages'])), '">';
5634 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...
5635
		echo '
5636
		<input type="hidden" name="themes" value="', base64_encode(safe_serialize($upcontext['themes'])), '">';
5637 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...
5638
		echo '
5639
		<input type="hidden" name="writable_files" value="', base64_encode(safe_serialize($upcontext['writable_files'])), '">';
5640
5641
	// Offer them the option to upgrade from YaBB SE?
5642
	if (!empty($upcontext['can_upgrade_yabbse']))
5643
		echo '
5644
		<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>';
5645
5646
	// We'll want a continue button... assuming chmod is OK (Otherwise let them use connect!)
5647
	if (empty($upcontext['chmod']['files']) || $upcontext['is_test'])
5648
		$upcontext['continue'] = 1;
5649
}
5650
5651
// Template for the database backup tool/
5652 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...
5653
{
5654
	global $upcontext, $support_js, $is_debug;
5655
5656
	echo '
5657
			<h3>Converting data from serialize to JSON...</h3>';
5658
5659
	echo '
5660
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
5661
			<input type="hidden" name="json_done" id="json_done" value="0">
5662
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
5663
			<span id="debuginfo"></span>';
5664
5665
	// Dont any tables so far?
5666
	if (!empty($upcontext['previous_tables']))
5667
		foreach ($upcontext['previous_tables'] as $table)
5668
			echo '
5669
			<br>Completed Table: &quot;', $table, '&quot;.';
5670
5671
	echo '
5672
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
5673
			<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>';
5674
5675
	// Continue please!
5676
	$upcontext['continue'] = $support_js ? 2 : 1;
5677
5678
	// If javascript allows we want to do this using XML.
5679
	if ($support_js)
5680
	{
5681
		echo '
5682
		<script>
5683
			var lastTable = ', $upcontext['cur_table_num'], ';
5684
			function getNextTables()
5685
			{
5686
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
5687
			}
5688
5689
			// Got an update!
5690
			function onBackupUpdate(oXMLDoc)
5691
			{
5692
				var sCurrentTableName = "";
5693
				var iTableNum = 0;
5694
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
5695
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
5696
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
5697
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
5698
5699
				// Update the page.
5700
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
5701
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
5702
				lastTable = iTableNum;
5703
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
5704
5705
		// If debug flood the screen.
5706
		if ($is_debug)
5707
			echo '
5708
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');';
5709
5710
		echo '
5711
				// Get the next update...
5712
				if (iTableNum == ', $upcontext['table_count'], ')
5713
				{
5714
					document.getElementById(\'commess\').style.display = "";
5715
					document.getElementById(\'current_tab_div\').style.display = "none";
5716
					document.getElementById(\'contbutt\').disabled = 0;
5717
					document.getElementById(\'json_done\').value = 1;
5718
				}
5719
				else
5720
					getNextTables();
5721
			}
5722
			getNextTables();
5723
		</script>';
5724
	}
5725
}
5726
5727
function template_serialize_json_xml()
5728
{
5729
	global $upcontext;
5730
5731
	echo '
5732
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
5733
}
5734
5735
function template_upgrade_complete()
5736
{
5737
	global $upcontext, $upgradeurl, $settings, $boardurl;
5738
5739
	echo '
5740
	<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>
5741
	<form action="', $boardurl, '/index.php">';
5742
5743
	if (!empty($upcontext['can_delete_script']))
5744
		echo '
5745
			<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>
5746
			<script>
5747
				function doTheDelete(theCheck)
5748
				{
5749
					var theImage = document.getElementById ? document.getElementById("delete_upgrader") : document.all.delete_upgrader;
5750
5751
					theImage.src = "', $upgradeurl, '?delete=1&ts_" + (new Date().getTime());
5752
					theCheck.disabled = true;
5753
				}
5754
			</script>
5755
			<img src="', $settings['default_theme_url'], '/images/blank.png" alt="" id="delete_upgrader"><br>';
5756
5757
	echo '<br>
5758
			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>
5759
			<br>
5760
			Best of luck,<br>
5761
			Simple Machines';
5762
}
5763
5764
/**
5765
 * Convert MySQL (var)char ip col to binary
5766
 * newCol needs to be a varbinary(16) null able field
5767
 * return true or false
5768
 */
5769
function MySQLConvertOldIp($targetTable,$oldCol,$newCol)
5770
{
5771
	global $smcFunc;
5772
	
5773
	//mysql default max length is 1mb http://dev.mysql.com/doc/refman/5.1/en/packet-too-large.html
5774
	$max = 10000;
5775
	$arIp = array();
5776
	
5777
	$request = $smcFunc['db_query']('', 'DROP TABLE IF EXISTS {db_prefix}ip_table');
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...
5778
	
5779
	$request = $smcFunc['db_query']('', 'CREATE TABLE {db_prefix}ip_table
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...
5780
    (oldip varchar(255), newip varbinary(16) )
5781
    ENGINE = MEMORY');
5782
	
5783
	$request = $smcFunc['db_query']('', 'SELECT DISTINCT '.$oldCol.' FROM {db_prefix}'.$targetTable);
5784
	while($row = $smcFunc['db_fetch_assoc']($request))
5785
		$arIp[] = $row[$oldCol]; 
5786
	$smcFunc['db_free_result']($request);
5787
	
5788
	$insertStart = 'INSERT INTO {db_prefix}ip_table(oldip, newip) VALUES';
5789
	$query = $insertStart;
5790
	$impArray = array();
5791
	$x = 0;
5792
	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...
5793
	{
5794
		$query .= $x > 0 ? ',':'';
5795
		$query .= '({string:ip'.$x.'},{inet:ip'.$x.'})';
5796
		$impArray['ip'.$x] = trim($arIp[$i]);
5797
5798
		$x++;
5799
		if($x > $max)
5800
		{
5801
			$request = $smcFunc['db_query']('', $query, $impArray);
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...
5802
			$x = 0;
5803
			$query = $insertStart;
5804
			$impArray = array();
5805
		}
5806
	}
5807
	$request = $smcFunc['db_query']('', $query, $impArray);
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...
5808
	
5809
	$request = $smcFunc['db_query']('', 'UPDATE {db_prefix}'.$targetTable.' a
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...
5810
	JOIN {db_prefix}ip_table b ON a.'.$oldCol.' = b.oldip
5811
	SET a.'.$newCol.' = b.newip');
5812
	
5813
	$request = $smcFunc['db_query']('', 'DROP TABLE IF EXISTS {db_prefix}ip_table');
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...
5814
	
5815
	return
5816
		true;
5817
}
5818
5819
?>
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...