Completed
Push — release-2.1 ( af8dd6...5b3c87 )
by Michael
20:47 queued 10:04
created

upgrade-helper.php ➔ quickFileWritable()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 27
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 13
nc 8
nop 1
dl 0
loc 27
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines http://www.simplemachines.org
8
 * @copyright 2017 Simple Machines and individual contributors
9
 * @license http://www.simplemachines.org/about/smf/license.php BSD
10
 *
11
 * @version 2.1 Beta 4
12
 *
13
 * This file contains helper functions for upgrade.php
14
 */
15
16
if (!defined('SMF_VERSION'))
17
	die('No direct access!');
18
19
/**
20
 * Clean the cache using the SMF 2.1 CacheAPI.
21
 * If coming from SMF 2.0 and below it should wipe the cache using the SMF backend.
22
 */
23
function upgrade_clean_cache()
24
{
25
	global $cacheAPI, $sourcedir;
26
27
	// Initialize the cache API if it does not have an instance yet.
28
	require_once($sourcedir . '/Load.php');
29
	if (empty($cacheAPI))
30
	{
31
		loadCacheAccelerator();
32
	}
33
34
	// Just through back to Load.php's clean_cache function.
35
	clean_cache();
36
}
37
38
/**
39
 * Returns a list of member groups. Used to upgrade 1.0 and 1.1.
40
 *
41
 * @return array
42
 */
43
function getMemberGroups()
44
{
45
	global $smcFunc;
46
	static $member_groups = array();
47
48
	if (!empty($member_groups))
49
		return $member_groups;
50
51
	$request = $smcFunc['db_query']('', '
52
		SELECT group_name, id_group
53
		FROM {db_prefix}membergroups
54
		WHERE id_group = {int:admin_group} OR id_group > {int:old_group}',
55
		array(
56
			'admin_group' => 1,
57
			'old_group' => 7,
58
			'db_error_skip' => true,
59
		)
60
	);
61
	if ($request === false)
62
	{
63
		$request = $smcFunc['db_query']('', '
64
			SELECT membergroup, id_group
65
			FROM {db_prefix}membergroups
66
			WHERE id_group = {int:admin_group} OR id_group > {int:old_group}',
67
			array(
68
				'admin_group' => 1,
69
				'old_group' => 7,
70
				'db_error_skip' => true,
71
			)
72
		);
73
	}
74
	while ($row = $smcFunc['db_fetch_row']($request))
75
		$member_groups[trim($row[0])] = $row[1];
76
	$smcFunc['db_free_result']($request);
77
78
	return $member_groups;
79
}
80
81
/**
82
 * Make files writable. First try to use regular chmod, but if that fails, try to use FTP.
83
 *
84
 * @param $files
85
 * @return bool
86
 */
87
function makeFilesWritable(&$files)
88
{
89
	global $upcontext, $boarddir, $sourcedir;
90
91
	if (empty($files))
92
		return true;
93
94
	$failure = false;
95
	// On linux, it's easy - just use is_writable!
96
	if (substr(__FILE__, 1, 2) != ':\\')
97
	{
98
		$upcontext['systemos'] = 'linux';
99
100
		foreach ($files as $k => $file)
101
		{
102
			// Some files won't exist, try to address up front
103
			if (!file_exists($file))
104
				@touch($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...
105
			// NOW do the writable check...
106
			if (!is_writable($file))
107
			{
108
				@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...
109
110
				// Well, 755 hopefully worked... if not, try 777.
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
111
				if (!is_writable($file) && !@chmod($file, 0777))
112
					$failure = true;
113
				// Otherwise remove it as it's good!
114
				else
115
					unset($files[$k]);
116
			}
117
			else
118
				unset($files[$k]);
119
		}
120
	}
121
	// Windows is trickier.  Let's try opening for r+...
122
	else
123
	{
124
		$upcontext['systemos'] = 'windows';
125
126
		foreach ($files as $k => $file)
127
		{
128
			// Folders can't be opened for write... but the index.php in them can ;).
129
			if (is_dir($file))
130
				$file .= '/index.php';
131
132
			// Funny enough, chmod actually does do something on windows - it removes the read only attribute.
133
			@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...
134
			$fp = @fopen($file, 'r+');
135
136
			// Hmm, okay, try just for write in that case...
137
			if (!$fp)
138
				$fp = @fopen($file, 'w');
139
140
			if (!$fp)
141
				$failure = true;
142
			else
143
				unset($files[$k]);
144
			@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...
145
		}
146
	}
147
148
	if (empty($files))
149
		return true;
150
151
	if (!isset($_SERVER))
152
		return !$failure;
153
154
	// What still needs to be done?
155
	$upcontext['chmod']['files'] = $files;
156
157
	// If it's windows it's a mess...
158
	if ($failure && substr(__FILE__, 1, 2) == ':\\')
159
	{
160
		$upcontext['chmod']['ftp_error'] = 'total_mess';
161
162
		return false;
163
	}
164
	// We're going to have to use... FTP!
165
	elseif ($failure)
166
	{
167
		// Load any session data we might have...
168
		if (!isset($_POST['ftp_username']) && isset($_SESSION['installer_temp_ftp']))
169
		{
170
			$upcontext['chmod']['server'] = $_SESSION['installer_temp_ftp']['server'];
171
			$upcontext['chmod']['port'] = $_SESSION['installer_temp_ftp']['port'];
172
			$upcontext['chmod']['username'] = $_SESSION['installer_temp_ftp']['username'];
173
			$upcontext['chmod']['password'] = $_SESSION['installer_temp_ftp']['password'];
174
			$upcontext['chmod']['path'] = $_SESSION['installer_temp_ftp']['path'];
175
		}
176
		// Or have we submitted?
177
		elseif (isset($_POST['ftp_username']))
178
		{
179
			$upcontext['chmod']['server'] = $_POST['ftp_server'];
180
			$upcontext['chmod']['port'] = $_POST['ftp_port'];
181
			$upcontext['chmod']['username'] = $_POST['ftp_username'];
182
			$upcontext['chmod']['password'] = $_POST['ftp_password'];
183
			$upcontext['chmod']['path'] = $_POST['ftp_path'];
184
		}
185
186
		require_once($sourcedir . '/Class-Package.php');
187
		if (isset($upcontext['chmod']['username']))
188
		{
189
			$ftp = new ftp_connection($upcontext['chmod']['server'], $upcontext['chmod']['port'], $upcontext['chmod']['username'], $upcontext['chmod']['password']);
190
191
			if ($ftp->error === false)
192
			{
193
				// Try it without /home/abc just in case they messed up.
194
				if (!$ftp->chdir($upcontext['chmod']['path']))
195
				{
196
					$upcontext['chmod']['ftp_error'] = $ftp->last_message;
197
					$ftp->chdir(preg_replace('~^/home[2]?/[^/]+?~', '', $upcontext['chmod']['path']));
198
				}
199
			}
200
		}
201
202
		if (!isset($ftp) || $ftp->error !== false)
203
		{
204
			if (!isset($ftp))
205
				$ftp = new ftp_connection(null);
206
			// Save the error so we can mess with listing...
207
			elseif ($ftp->error !== false && !isset($upcontext['chmod']['ftp_error']))
208
				$upcontext['chmod']['ftp_error'] = $ftp->last_message === null ? '' : $ftp->last_message;
209
210
			list ($username, $detect_path, $found_path) = $ftp->detect_path(dirname(__FILE__));
211
212
			if ($found_path || !isset($upcontext['chmod']['path']))
213
				$upcontext['chmod']['path'] = $detect_path;
214
215
			if (!isset($upcontext['chmod']['username']))
216
				$upcontext['chmod']['username'] = $username;
217
218
			// Don't forget the login token.
219
			$upcontext += createToken('login');
220
221
			return false;
222
		}
223
		else
224
		{
225
			// We want to do a relative path for FTP.
226
			if (!in_array($upcontext['chmod']['path'], array('', '/')))
227
			{
228
				$ftp_root = strtr($boarddir, array($upcontext['chmod']['path'] => ''));
229
				if (substr($ftp_root, -1) == '/' && ($upcontext['chmod']['path'] == '' || $upcontext['chmod']['path'][0] === '/'))
230
					$ftp_root = substr($ftp_root, 0, -1);
231
			}
232
			else
233
				$ftp_root = $boarddir;
234
235
			// Save the info for next time!
236
			$_SESSION['installer_temp_ftp'] = array(
237
				'server' => $upcontext['chmod']['server'],
238
				'port' => $upcontext['chmod']['port'],
239
				'username' => $upcontext['chmod']['username'],
240
				'password' => $upcontext['chmod']['password'],
241
				'path' => $upcontext['chmod']['path'],
242
				'root' => $ftp_root,
243
			);
244
245
			foreach ($files as $k => $file)
246
			{
247
				if (!is_writable($file))
248
					$ftp->chmod($file, 0755);
249
				if (!is_writable($file))
250
					$ftp->chmod($file, 0777);
251
252
				// Assuming that didn't work calculate the path without the boarddir.
253
				if (!is_writable($file))
254
				{
255
					if (strpos($file, $boarddir) === 0)
256
					{
257
						$ftp_file = strtr($file, array($_SESSION['installer_temp_ftp']['root'] => ''));
258
						$ftp->chmod($ftp_file, 0755);
259
						if (!is_writable($file))
260
							$ftp->chmod($ftp_file, 0777);
261
						// Sometimes an extra slash can help...
262
						$ftp_file = '/' . $ftp_file;
263
						if (!is_writable($file))
264
							$ftp->chmod($ftp_file, 0755);
265
						if (!is_writable($file))
266
							$ftp->chmod($ftp_file, 0777);
267
					}
268
				}
269
270
				if (is_writable($file))
271
					unset($files[$k]);
272
			}
273
274
			$ftp->close();
275
		}
276
	}
277
278
	// What remains?
279
	$upcontext['chmod']['files'] = $files;
280
281
	if (empty($files))
282
		return true;
283
284
	return false;
285
}
286
287
/**
288
 * The quick version of makeFilesWritable, which does not support FTP.
289
 *
290
 * @param string $file
291
 * @return bool
292
 */
293
function quickFileWritable($file)
294
{
295
296
	// Some files won't exist, try to address up front
297
	if (!file_exists($file))
298
		@touch($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...
299
300
	// NOW do the writable check...
301
	if (is_writable($file))
302
		return true;
303
304
	@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...
305
306
	// Try 755 and 775 first since 777 doesn't always work and could be a risk...
307
	$chmod_values = array(0755, 0775, 0777);
308
309
	foreach ($chmod_values as $val)
310
	{
311
		// If it's writable, break out of the loop
312
		if (is_writable($file))
313
			break;
314
		else
315
			@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...
316
	}
317
318
	return is_writable($file);
319
}
320
321
/**
322
 * UTF-8 aware strtolower function.
323
 *
324
 * @param $string
325
 * @return string
326
 */
327
function smf_strtolower($string)
328
{
329
	return mb_strtolower($string, 'UTF-8');
330
}
331
332
/**
333
 * Prints an error to stderr.
334
 *
335
 * @param $message
336
 * @param bool $fatal
337
 */
338
function print_error($message, $fatal = false)
339
{
340
	static $fp = null;
341
342
	if ($fp === null)
343
		$fp = fopen('php://stderr', 'wb');
344
345
	fwrite($fp, $message . "\n");
346
347
	if ($fatal)
348
		exit;
349
}
350
351
/**
352
 * Throws a graphical error message.
353
 *
354
 * @param $message
355
 * @return bool
356
 */
357
function throw_error($message)
358
{
359
	global $upcontext;
360
361
	$upcontext['error_msg'] = $message;
362
	$upcontext['sub_template'] = 'error_message';
363
364
	return false;
365
}
366
367
/**
368
 * Database functions below here.
369
 */
370
/**
371
 * @param $rs
372
 * @return array|null
373
 */
374
function smf_mysql_fetch_assoc($rs)
375
{
376
	return mysqli_fetch_assoc($rs);
377
}
378
379
/**
380
 * @param $rs
381
 * @return array|null
382
 */
383
function smf_mysql_fetch_row($rs)
384
{
385
	return mysqli_fetch_row($rs);
386
}
387
388
/**
389
 * @param $rs
390
 */
391
function smf_mysql_free_result($rs)
392
{
393
	mysqli_free_result($rs);
394
}
395
396
/**
397
 * @param $rs
398
 * @return int|string
399
 */
400
function smf_mysql_insert_id($rs)
401
{
402
	return mysqli_insert_id($rs);
403
}
404
405
/**
406
 * @param $rs
407
 * @return int
408
 */
409
function smf_mysql_num_rows($rs)
410
{
411
	return mysqli_num_rows($rs);
412
}
413
414
/**
415
 * @param $string
416
 */
417
function smf_mysql_real_escape_string($string)
418
{
419
	global $db_connection;
420
	mysqli_real_escape_string($db_connection, $string);
421
}