Completed
Push — release-2.1 ( aa21c4...7040ad )
by Mathias
09:20
created

upgrade-helper.php ➔ smf_mysql_fetch_row()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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