Issues (661)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

php/elFinderVolumeLocalFileSystem.class.php (54 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
// Implement similar functionality in PHP 5.2 or 5.3
4
// http://php.net/manual/class.recursivecallbackfilteriterator.php#110974
5
if (! class_exists('RecursiveCallbackFilterIterator', false)) {
6
	class RecursiveCallbackFilterIterator extends RecursiveFilterIterator {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
7
	   
8
	    public function __construct ( RecursiveIterator $iterator, $callback ) {
9
	        $this->callback = $callback;
0 ignored issues
show
The property callback does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
10
	        parent::__construct($iterator);
11
	    }
12
	   
13
	    public function accept () {
14
	        $callback = $this->callback;
15
	        return $callback(parent::current(), parent::key(), parent::getInnerIterator());
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (current() instead of accept()). Are you sure this is correct? If so, you might want to change this to $this->current().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
Comprehensibility Bug introduced by
It seems like you call parent on a different method (key() instead of accept()). Are you sure this is correct? If so, you might want to change this to $this->key().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
Comprehensibility Bug introduced by
It seems like you call parent on a different method (getInnerIterator() instead of accept()). Are you sure this is correct? If so, you might want to change this to $this->getInnerIterator().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
16
	    }
17
	   
18
	    public function getChildren () {
19
	        return new self($this->getInnerIterator()->getChildren(), $this->callback);
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Iterator as the method getChildren() does only exist in the following implementations of said interface: ParentIterator, RecursiveArrayIterator, RecursiveCachingIterator, RecursiveCallbackFilterIterator, RecursiveDirectoryIterator, RecursiveFilterIterator, RecursiveRegexIterator, SimpleXMLIterator, SplFileObject, SplTempFileObject.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
20
	    }
21
	}
22
}
23
24
/**
25
 * elFinder driver for local filesystem.
26
 *
27
 * @author Dmitry (dio) Levashov
28
 * @author Troex Nevelin
29
 **/
30
class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
31
	
32
	/**
33
	 * Driver id
34
	 * Must be started from letter and contains [a-z0-9]
35
	 * Used as part of volume id
36
	 *
37
	 * @var string
38
	 **/
39
	protected $driverId = 'l';
40
	
41
	/**
42
	 * Required to count total archive files size
43
	 *
44
	 * @var int
45
	 **/
46
	protected $archiveSize = 0;
47
	
48
	/**
49
	 * Current query word on doSearch
50
	 *
51
	 * @var string
52
	 **/
53
	private $doSearchCurrentQuery = '';
54
	
55
	/**
56
	 * Constructor
57
	 * Extend options with required fields
58
	 *
59
	 * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
60
	 * @author Dmitry (dio) Levashov
61
	 **/
62
	public function __construct() {
63
		$this->options['alias']    = '';              // alias to replace root dir name
64
		$this->options['dirMode']  = 0755;            // new dirs mode
65
		$this->options['fileMode'] = 0644;            // new files mode
66
		$this->options['quarantine'] = '.quarantine';  // quarantine folder name - required to check archive (must be hidden)
67
		$this->options['maxArcFilesSize'] = 0;        // max allowed archive files size (0 - no limit)
68
		$this->options['rootCssClass'] = 'elfinder-navbar-root-local';
69
	}
70
	
71
	/*********************************************************************/
72
	/*                        INIT AND CONFIGURE                         */
73
	/*********************************************************************/
74
	
75
	/**
76
	 * Prepare driver before mount volume.
77
	 * Return true if volume is ready.
78
	 *
79
	 * @return bool
80
	 **/
81
	protected function init() {
82
		// Normalize directory separator for windows
83
		if (DIRECTORY_SEPARATOR !== '/') {
84
			foreach(array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) {
85
				if (!empty($this->options[$key])) {
86
					$this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]);
87
				}
88
			}
89
		}
90
		if (!$cwd = getcwd()) {
91
			return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().');
92
		}
93
		// detect systemRoot
94
		if (!isset($this->options['systemRoot'])) {
95
			if ($cwd[0] === $this->separator || $this->root[0] === $this->separator) {
96
				$this->systemRoot = $this->separator;
97
			} else if (preg_match('/^([a-zA-Z]:'.preg_quote($this->separator, '/').')/', $this->root, $m)) {
98
				$this->systemRoot = $m[1];
99
			} else if (preg_match('/^([a-zA-Z]:'.preg_quote($this->separator, '/').')/', $cwd, $m)) {
100
				$this->systemRoot = $m[1];
101
			}
102
		}
103
		$this->root = $this->getFullPath($this->root, $cwd);
104
		if (!empty($this->options['startPath'])) {
105
			$this->options['startPath'] = $this->getFullPath($this->options['startPath'], $cwd);
106
		}
107
		
108
		if (is_null($this->options['syncChkAsTs'])) {
109
			$this->options['syncChkAsTs'] = true;
110
		}
111
		if (is_null($this->options['syncCheckFunc'])) {
112
			$this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify');
113
		}
114
		
115
		return true;
116
	}
117
	
118
	/**
119
	 * Configure after successfull mount.
120
	 *
121
	 * @return void
122
	 * @author Dmitry (dio) Levashov
123
	 **/
124
	protected function configure() {
125
		$root = $this->stat($this->root);
126
		
127
		// chek thumbnails path
128
		if ($this->options['tmbPath']) {
129
			$this->options['tmbPath'] = strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false
130
				// tmb path set as dirname under root dir
131
				? $this->_abspath($this->options['tmbPath'])
132
				// tmb path as full path
133
				: $this->_normpath($this->options['tmbPath']);
134
		}
135
136
		parent::configure();
137
		
138
		// set $this->tmp by options['tmpPath']
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% 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...
139 View Code Duplication
		if (!empty($this->options['tmpPath'])) {
0 ignored issues
show
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...
140
			if ((is_dir($this->options['tmpPath']) || @mkdir($this->options['tmpPath'], 0755, true)) && is_writable($this->options['tmpPath'])) {
141
				$this->tmp = $this->options['tmpPath'];
0 ignored issues
show
The property tmp does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
142
			}
143
		}
144
		
145
		// if no thumbnails url - try detect it
146
		if ($root['read'] && !$this->tmbURL && $this->URL) {
147
			if (strpos($this->tmbPath, $this->root) === 0) {
148
				$this->tmbURL = $this->URL.str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root)+1));
149
				if (preg_match("|[^/?&=]$|", $this->tmbURL)) {
150
					$this->tmbURL .= '/';
151
				}
152
			}
153
		}
154
155
		// check quarantine dir
156
		$this->quarantine = '';
0 ignored issues
show
The property quarantine does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
157
		if (!empty($this->options['quarantine'])) {
158
			if (is_dir($this->options['quarantine'])) {
159
				if (is_writable($this->options['quarantine'])) {
160
					$this->quarantine = $this->options['quarantine'];
161
				}
162
				$this->options['quarantine'] = '';
163
			} else {
164
				$this->quarantine = $this->_abspath($this->options['quarantine']);
165
				if ((!is_dir($this->quarantine) && !$this->_mkdir($this->root, $this->options['quarantine'])) || !is_writable($this->quarantine)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_mkdir($this->roo...>options['quarantine']) of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
166
					$this->options['quarantine'] = $this->quarantine = '';
167
				}
168
			}
169
		}
170
		
171
		if (!$this->quarantine) {
172
			$this->archivers['extract'] = array();
173
			$this->disabled[] = 'extract';
174
		}
175
		
176
		if ($this->options['quarantine']) {
177
			$this->attributes[] = array(
178
					'pattern' => '~^'.preg_quote(DIRECTORY_SEPARATOR.$this->options['quarantine']).'$~',
179
					'read'    => false,
180
					'write'   => false,
181
					'locked'  => true,
182
					'hidden'  => true
183
			);
184
		}
185
	}
186
	
187
	/**
188
	 * Long pooling sync checker
189
	 * This function require server command `inotifywait`
190
	 * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php
191
	 * 
192
	 * @param string     $path
193
	 * @param int        $standby
194
	 * @param number     $compare
195
	 * @return number|bool
196
	 */
197
	public function localFileSystemInotify($path, $standby, $compare) {
198
		if (isset($this->sessionCache['localFileSystemInotify_disable'])) {
199
			return false;
200
		}
201
		$path = realpath($path);
202
		$mtime = filemtime($path);
203
		if ($mtime != $compare) {
204
			return $mtime;
205
		}
206
		$inotifywait = defined('ELFINER_INOTIFYWAIT_PATH')? ELFINER_INOTIFYWAIT_PATH : 'inotifywait';
207
		$path = escapeshellarg($path);
208
		$standby = max(1, intval($standby));
209
		$cmd = $inotifywait.' '.$path.' -t '.$standby.' -e moved_to,moved_from,move,create,delete,delete_self';
210
		$this->procExec($cmd , $o, $r);
211
		if ($r === 0) {
212
			// changed
213
			clearstatcache();
214
			return filemtime($path);
215
		} else if ($r === 2) {
216
			// not changed (timeout)
217
			return $compare;
218
		}
219
		// error
220
		// cache to $_SESSION
221
		$sessionStart = $this->sessionRestart();
222
		if ($sessionStart) {
223
			$this->sessionCache['localFileSystemInotify_disable'] = true;
224
			session_write_close();
225
		}
226
		
227
		return false;
228
	}
229
	
230
	/*********************************************************************/
231
	/*                               FS API                              */
232
	/*********************************************************************/
233
234
	/*********************** paths/urls *************************/
235
	
236
	/**
237
	 * Return parent directory path
238
	 *
239
	 * @param  string  $path  file path
240
	 * @return string
241
	 * @author Dmitry (dio) Levashov
242
	 **/
243
	protected function _dirname($path) {
244
		return dirname($path);
245
	}
246
247
	/**
248
	 * Return file name
249
	 *
250
	 * @param  string  $path  file path
251
	 * @return string
252
	 * @author Dmitry (dio) Levashov
253
	 **/
254
	protected function _basename($path) {
255
		return basename($path);
256
	}
257
258
	/**
259
	 * Join dir name and file name and retur full path
260
	 *
261
	 * @param  string  $dir
262
	 * @param  string  $name
263
	 * @return string
264
	 * @author Dmitry (dio) Levashov
265
	 **/
266
	protected function _joinPath($dir, $name) {
267
		return rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name;
268
	}
269
	
270
	/**
271
	 * Return normalized path, this works the same as os.path.normpath() in Python
272
	 *
273
	 * @param  string  $path  path
274
	 * @return string
275
	 * @author Troex Nevelin
276
	 **/
277
	protected function _normpath($path) {
278
		if (empty($path)) {
279
			return '.';
280
		}
281
		
282
		$changeSep = (DIRECTORY_SEPARATOR !== '/');
283
		if ($changeSep) {
284
			$path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
285
		}
286
287
		if (strpos($path, '/') === 0) {
288
			$initial_slashes = true;
289
		} else {
290
			$initial_slashes = false;
291
		}
292
			
293 View Code Duplication
		if (($initial_slashes) 
0 ignored issues
show
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...
294
		&& (strpos($path, '//') === 0) 
295
		&& (strpos($path, '///') === false)) {
296
			$initial_slashes = 2;
297
		}
298
			
299
		$initial_slashes = (int) $initial_slashes;
300
301
		$comps = explode('/', $path);
302
		$new_comps = array();
303 View Code Duplication
		foreach ($comps as $comp) {
0 ignored issues
show
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...
304
			if (in_array($comp, array('', '.'))) {
305
				continue;
306
			}
307
				
308
			if (($comp != '..') 
309
			|| (!$initial_slashes && !$new_comps) 
0 ignored issues
show
Bug Best Practice introduced by
The expression $new_comps of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
310
			|| ($new_comps && (end($new_comps) == '..'))) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $new_comps of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
311
				array_push($new_comps, $comp);
312
			} elseif ($new_comps) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $new_comps of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
313
				array_pop($new_comps);
314
			}
315
		}
316
		$comps = $new_comps;
317
		$path = implode('/', $comps);
318
		if ($initial_slashes) {
319
			$path = str_repeat('/', $initial_slashes) . $path;
320
		}
321
		
322
		if ($changeSep) {
323
			$path = str_replace('/', DIRECTORY_SEPARATOR, $path);
324
		}
325
		
326
		return $path ? $path : '.';
327
	}
328
	
329
	/**
330
	 * Return file path related to root dir
331
	 *
332
	 * @param  string  $path  file path
333
	 * @return string
334
	 * @author Dmitry (dio) Levashov
335
	 **/
336 View Code Duplication
	protected function _relpath($path) {
0 ignored issues
show
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...
337
		if ($path === $this->root) {
338
			return '';
339
		} else {
340
			if (strpos($path, $this->root) === 0) {
341
				return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR);
342
			} else {
343
				// for link
344
				return $path;
345
			}
346
		}
347
	}
348
	
349
	/**
350
	 * Convert path related to root dir into real path
351
	 *
352
	 * @param  string  $path  file path
353
	 * @return string
354
	 * @author Dmitry (dio) Levashov
355
	 **/
356
	protected function _abspath($path) {
357
		if ($path === DIRECTORY_SEPARATOR) {
358
			return $this->root;
359
		} else {
360
			if ($path[0] === DIRECTORY_SEPARATOR) {
361
				// for link
362
				return $path;
363
			} else {
364
				return $this->_joinPath($this->root, $path);
365
			}
366
		}
367
	}
368
	
369
	/**
370
	 * Return fake path started from root dir
371
	 *
372
	 * @param  string  $path  file path
373
	 * @return string
374
	 * @author Dmitry (dio) Levashov
375
	 **/
376
	protected function _path($path) {
377
		return $this->rootName.($path == $this->root ? '' : $this->separator.$this->_relpath($path));
378
	}
379
	
380
	/**
381
	 * Return true if $path is children of $parent
382
	 *
383
	 * @param  string  $path    path to check
384
	 * @param  string  $parent  parent path
385
	 * @return bool
386
	 * @author Dmitry (dio) Levashov
387
	 **/
388
	protected function _inpath($path, $parent) {
389
		$cwd = getcwd();
390
		$real_path   = $this->getFullPath($path,   $cwd);
391
		$real_parent = $this->getFullPath($parent, $cwd);
392
		if ($real_path && $real_parent) {
393
			return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR) === 0;
394
		}
395
		return false;
396
	}
397
	
398
	
399
	
400
	/***************** file stat ********************/
401
402
	/**
403
	 * Return stat for given path.
404
	 * Stat contains following fields:
405
	 * - (int)    size    file size in b. required
406
	 * - (int)    ts      file modification time in unix time. required
407
	 * - (string) mime    mimetype. required for folders, others - optionally
408
	 * - (bool)   read    read permissions. required
409
	 * - (bool)   write   write permissions. required
410
	 * - (bool)   locked  is object locked. optionally
411
	 * - (bool)   hidden  is object hidden. optionally
412
	 * - (string) alias   for symlinks - link target path relative to root path. optionally
413
	 * - (string) target  for symlinks - link target path. optionally
414
	 *
415
	 * If file does not exists - returns empty array or false.
416
	 *
417
	 * @param  string  $path    file path 
418
	 * @return array|false
419
	 * @author Dmitry (dio) Levashov
420
	 **/
421
	protected function _stat($path) {
422
		
423
		static $statOwner;
424
		if (is_null($statOwner)) {
425
			$statOwner = (!empty($this->options['statOwner']));
426
		}
427
		
428
		$stat = array();
429
430
		if (!file_exists($path) && !is_link($path)) {
431
			return $stat;
432
		}
433
434
		//Verifies the given path is the root or is inside the root. Prevents directory traveral.
435
		if (!$this->_inpath($path, $this->root)) {
436
			return $stat;
437
		}
438
439
		$gid = $uid = 0;
0 ignored issues
show
$uid 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...
$gid 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...
440
		$stat['isowner'] = false;
441
		$linkreadable = false;
442
		if ($path != $this->root && is_link($path)) {
443
			if (!($target = $this->readlink($path))
444
			|| $target == $path) {
445
				if (is_null($target)) {
446
					$stat = array();
447
					return $stat;
448
				} else {
449
					$stat['mime']  = 'symlink-broken';
450
					$target = readlink($path);
451
					$lstat = lstat($path);
452
					$ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
453
					$linkreadable = !empty($ostat['isowner']);
454
				}
455
			}
456
			$stat['alias'] = $this->_path($target);
457
			$stat['target'] = $target;
458
		}
459
		$size = sprintf('%u', @filesize($path));
460
		$stat['ts'] = filemtime($path);
461
		if ($statOwner) {
462
			$fstat = stat($path);
463
			$uid = $fstat['uid'];
464
			$gid = $fstat['gid'];
465
			$stat['perm'] = substr((string)decoct($fstat['mode']), -4);
466
			$stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
467
		}
468
		
469
		$dir = is_dir($path);
470
		
471
		if (!isset($stat['mime'])) {
472
			$stat['mime'] = $dir ? 'directory' : $this->mimetype($path);
473
		}
474
		//logical rights first
475
		$stat['read'] = ($linkreadable || is_readable($path))? null : false;
476
		$stat['write'] = is_writable($path)? null : false;
477
478
		if (is_null($stat['read'])) {
479
			$stat['size'] = $dir ? 0 : $size;
480
		}
481
		
482
		return $stat;
483
	}
484
	
485
	/**
486
	 * Get stat `owner`, `group` and `isowner` by `uid` and `gid`
487
	 * Sub-fuction of _stat() and _scandir()
488
	 * 
489
	 * @param integer $uid
490
	 * @param integer $gid
491
	 * @return array  stat
492
	 */
493
	protected function getOwnerStat($uid, $gid) {
494
		static $names = null;
495
		static $phpuid = null;
496
		
497
		if (is_null($names)) {
498
			$names = array('uid' => array(), 'gid' =>array());
499
		}
500
		if (is_null($phpuid)) {
501
			if (is_callable('posix_getuid')) {
502
				$phpuid = posix_getuid();
503
			} else {
504
				$phpuid = 0;
505
			}
506
		}
507
		
508
		$stat = array();
509
		
510
		if ($uid) {
511
			$stat['isowner'] = ($phpuid == $uid);
512 View Code Duplication
			if (isset($names['uid'][$uid])) {
0 ignored issues
show
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...
513
				$stat['owner'] = $names['uid'][$uid];
514
			} else if (is_callable('posix_getpwuid')) {
515
				$pwuid = posix_getpwuid($uid);
516
				$stat['owner'] = $names['uid'][$uid] = $pwuid['name'];
517
			} else {
518
				$stat['owner'] = $names['uid'][$uid] = $uid;
519
			}
520
		}
521 View Code Duplication
		if ($gid) {
0 ignored issues
show
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...
522
			if (isset($names['gid'][$gid])) {
523
				$stat['group'] = $names['gid'][$gid];
524
			} else if (is_callable('posix_getgrgid')) {
525
				$grgid = posix_getgrgid($gid);
526
				$stat['group'] = $names['gid'][$gid] = $grgid['name'];
527
			} else {
528
				$stat['group'] = $names['gid'][$gid] = $gid;
529
			}
530
		}
531
		
532
		return $stat;
533
	}
534
535
	/**
536
	 * Return true if path is dir and has at least one childs directory
537
	 *
538
	 * @param  string  $path  dir path
539
	 * @return bool
540
	 * @author Dmitry (dio) Levashov
541
	 **/
542
	protected function _subdirs($path) {
543
544
		$dirs = false;
545
		if (is_dir($path)) {
546
			$dirItr = new ParentIterator(
547
				new RecursiveDirectoryIterator($path,
548
					FilesystemIterator::SKIP_DOTS |
549
					(defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS')?
550
						RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
551
				)
552
			);
553
			$dirItr->rewind();
554
			if ($dirItr->hasChildren()) {
555
				$dirs = true;
556
				$name = $dirItr->getSubPathName();
557
				while($name) {
558
					if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) {
559
						$dirs = false;
560
						$dirItr->next();
561
						$name = $dirItr->getSubPathName();
562
						continue;
563
					}
564
					$dirs = true;
565
					break;
566
				}
567
			}
568
		}
569
		return $dirs;
570
	}
571
	
572
	/**
573
	 * Return object width and height
574
	 * Usualy used for images, but can be realize for video etc...
575
	 *
576
	 * @param  string  $path  file path
577
	 * @param  string  $mime  file mime type
578
	 * @return string
579
	 * @author Dmitry (dio) Levashov
580
	 **/
581
	protected function _dimensions($path, $mime) {
582
		clearstatcache();
583
		return strpos($mime, 'image') === 0 && ($s = @getimagesize($path)) !== false 
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression strpos($mime, 'image') =... . 'x' . $s[1] : false; of type string|false adds false to the return on line 583 which is incompatible with the return type declared by the abstract method elFinderVolumeDriver::_dimensions of type string. It seems like you forgot to handle an error condition.
Loading history...
584
			? $s[0].'x'.$s[1] 
0 ignored issues
show
The variable $s 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...
585
			: false;
586
	}
587
	/******************** file/dir content *********************/
588
	
589
	/**
590
	 * Return symlink target file
591
	 *
592
	 * @param  string  $path  link path
593
	 * @return string
594
	 * @author Dmitry (dio) Levashov
595
	 **/
596
	protected function readlink($path) {
597
		if (!($target = @readlink($path))) {
598
			return null;
599
		}
600
601
		if (strpos($target, $this->systemRoot) !== 0) {
602
			$target = $this->_joinPath(dirname($path), $target);
603
		}
604
605
		if (!file_exists($target)) {
606
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by elFinderVolumeLocalFileSystem::readlink of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
607
		}
608
		
609
		return $target;
610
	}
611
		
612
	/**
613
	 * Return files list in directory.
614
	 *
615
	 * @param  string  $path  dir path
616
	 * @return array
617
	 * @author Dmitry (dio) Levashov
618
	 **/
619
	protected function _scandir($path) {
620
		$files = array();
621
		$cache = array();
622
		$statOwner = (!empty($this->options['statOwner']));
623
		$dirItr = array();
624
		try {
625
			$dirItr = new DirectoryIterator($path);
626
		} catch (UnexpectedValueException $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
627
		
628
		foreach ($dirItr as $file) {
629
			try {
630
				if ($file->isDot()) { continue; }
631
				
632
				$files[] = $fpath = $file->getPathname();
633
				
634
				$br = false;
635
				$stat = array();
636
				
637
				$gid = $uid = 0;
638
				$stat['isowner'] = false;
639
				$linkreadable = false;
640
				if ($file->isLink()) {
641
					if (!($target = $this->readlink($fpath))
642
					|| $target == $fpath) {
643
						if (is_null($target)) {
644
							$stat = array();
645
							$br = true;
646
						} else {
647
							$_path = $fpath;
648
							$stat['mime']  = 'symlink-broken';
649
							$target = readlink($_path);
650
							$lstat = lstat($_path);
651
							$ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']);
652
							$linkreadable = !empty($ostat['isowner']);
653
							$dir = false;
654
							$stat['alias'] = $this->_path($target);
655
							$stat['target'] = $target;
656
						}
657
					} else {
658
						$dir = is_dir($target);
659
						$stat['alias'] = $this->_path($target);
660
						$stat['target'] = $target;
661
						$stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']);
662
					}
663
				} else {
664
					$dir = $file->isDir();
665
					$stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath);
666
				}
667
				$size = sprintf('%u', $file->getSize());
668
				$stat['ts'] = $file->getMTime();
669
				if (!$br) {
670
					if ($statOwner && !$linkreadable) {
671
						$uid = $file->getOwner();
672
						$gid = $file->getGroup();
673
						$stat['perm'] = substr((string)decoct($file->getPerms()), -4);
674
						$stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
675
					}
676
					
677
					//logical rights first
678
					$stat['read'] = ($linkreadable || $file->isReadable())? null : false;
679
					$stat['write'] = $file->isWritable()? null : false;
680
					
681
					if (is_null($stat['read'])) {
682
						$stat['size'] = $dir ? 0 : $size;
0 ignored issues
show
The variable $dir 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...
683
					}
684
					
685
				}
686
				
687
				$cache[] = array($fpath, $stat);
688
			} catch (RuntimeException $e) {
689
				continue;
690
			}
691
		}
692
		
693
		if ($cache) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $cache of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
694
			$cache = $this->convEncOut($cache, false);
695
			foreach($cache as $d) {
696
				$this->updateCache($d[0], $d[1]);
697
			}
698
		}
699
		
700
		return $files;
701
	}
702
		
703
	/**
704
	 * Open file and return file pointer
705
	 *
706
	 * @param  string  $path  file path
707
	 * @param  bool    $write open file for writing
0 ignored issues
show
There is no parameter named $write. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
708
	 * @return resource|false
709
	 * @author Dmitry (dio) Levashov
710
	 **/
711
	protected function _fopen($path, $mode='rb') {
712
		return @fopen($path, $mode);
713
	}
714
	
715
	/**
716
	 * Close opened file
717
	 *
718
	 * @param  resource  $fp  file pointer
719
	 * @return bool
720
	 * @author Dmitry (dio) Levashov
721
	 **/
722
	protected function _fclose($fp, $path='') {
723
		return @fclose($fp);
724
	}
725
	
726
	/********************  file/dir manipulations *************************/
727
	
728
	/**
729
	 * Create dir and return created dir path or false on failed
730
	 *
731
	 * @param  string  $path  parent dir path
732
	 * @param string  $name  new directory name
733
	 * @return string|bool
734
	 * @author Dmitry (dio) Levashov
735
	 **/
736 View Code Duplication
	protected function _mkdir($path, $name) {
0 ignored issues
show
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...
737
		$path = $this->_joinPath($path, $name);
738
739
		if (@mkdir($path)) {
740
			@chmod($path, $this->options['dirMode']);
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...
741
			clearstatcache();
742
			return $path;
743
		}
744
745
		return false;
746
	}
747
	
748
	/**
749
	 * Create file and return it's path or false on failed
750
	 *
751
	 * @param  string  $path  parent dir path
752
	 * @param string  $name  new file name
753
	 * @return string|bool
754
	 * @author Dmitry (dio) Levashov
755
	 **/
756 View Code Duplication
	protected function _mkfile($path, $name) {
0 ignored issues
show
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...
757
		$path = $this->_joinPath($path, $name);
758
		
759
		if (($fp = @fopen($path, 'w'))) {
760
			@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...
761
			@chmod($path, $this->options['fileMode']);
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...
762
			clearstatcache();
763
			return $path;
764
		}
765
		return false;
766
	}
767
	
768
	/**
769
	 * Create symlink
770
	 *
771
	 * @param  string  $source     file to link to
772
	 * @param  string  $targetDir  folder to create link in
773
	 * @param  string  $name       symlink name
774
	 * @return bool
775
	 * @author Dmitry (dio) Levashov
776
	 **/
777
	protected function _symlink($source, $targetDir, $name) {
778
		return @symlink($source, $this->_joinPath($targetDir, $name));
779
	}
780
	
781
	/**
782
	 * Copy file into another file
783
	 *
784
	 * @param  string  $source     source file path
785
	 * @param  string  $targetDir  target directory path
786
	 * @param  string  $name       new file name
787
	 * @return bool
788
	 * @author Dmitry (dio) Levashov
789
	 **/
790
	protected function _copy($source, $targetDir, $name) {
791
		$ret = copy($source, $this->_joinPath($targetDir, $name));
792
		$ret && clearstatcache();
793
		return $ret;
794
	}
795
	
796
	/**
797
	 * Move file into another parent dir.
798
	 * Return new file path or false.
799
	 *
800
	 * @param  string  $source  source file path
801
	 * @param  string  $target  target dir path
0 ignored issues
show
There is no parameter named $target. Did you maybe mean $targetDir?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
802
	 * @param  string  $name    file name
803
	 * @return string|bool
804
	 * @author Dmitry (dio) Levashov
805
	 **/
806
	protected function _move($source, $targetDir, $name) {
807
		$target = $this->_joinPath($targetDir, $name);
808
		$ret = @rename($source, $target) ? $target : false;
809
		$ret && clearstatcache();
0 ignored issues
show
Bug Best Practice introduced by
The expression $ret of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
810
		return $ret;
811
	}
812
		
813
	/**
814
	 * Remove file
815
	 *
816
	 * @param  string  $path  file path
817
	 * @return bool
818
	 * @author Dmitry (dio) Levashov
819
	 **/
820
	protected function _unlink($path) {
821
		$ret = @unlink($path);
822
		$ret && clearstatcache();
823
		return $ret;
824
	}
825
826
	/**
827
	 * Remove dir
828
	 *
829
	 * @param  string  $path  dir path
830
	 * @return bool
831
	 * @author Dmitry (dio) Levashov
832
	 **/
833
	protected function _rmdir($path) {
834
		$ret = @rmdir($path);
835
		$ret && clearstatcache();
836
		return $ret;
837
	}
838
	
839
	/**
840
	 * Create new file and write into it from file pointer.
841
	 * Return new file path or false on error.
842
	 *
843
	 * @param  resource  $fp   file pointer
844
	 * @param  string    $dir  target dir path
845
	 * @param  string    $name file name
846
	 * @param  array     $stat file stat (required by some virtual fs)
847
	 * @return bool|string
848
	 * @author Dmitry (dio) Levashov
849
	 **/
850
	protected function _save($fp, $dir, $name, $stat) {
851
		$path = $this->_joinPath($dir, $name);
852
853
		$meta = stream_get_meta_data($fp);
854
		$uri = isset($meta['uri'])? $meta['uri'] : '';
855
		if ($uri && @is_file($uri)) {
856
			fclose($fp);
857
			$isCmdPaste = ($this->ARGS['cmd'] === 'paste');
858
			$isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut']));
859
			if (($isCmdCopy || !@rename($uri, $path)) && !@copy($uri, $path)) {
860
				return false;
861
			}
862
			// re-create the source file for remove processing of paste command
863
			$isCmdPaste && !$isCmdCopy && touch($uri);
864
		} else {
865
			if (@file_put_contents($path, $fp, LOCK_EX) === false) {
866
				return false;
867
			}
868
		}
869
870
		@chmod($path, $this->options['fileMode']);
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...
871
		clearstatcache();
872
		return $path;
873
	}
874
	
875
	/**
876
	 * Get file contents
877
	 *
878
	 * @param  string  $path  file path
879
	 * @return string|false
880
	 * @author Dmitry (dio) Levashov
881
	 **/
882
	protected function _getContents($path) {
883
		return file_get_contents($path);
884
	}
885
	
886
	/**
887
	 * Write a string to a file
888
	 *
889
	 * @param  string  $path     file path
890
	 * @param  string  $content  new file content
891
	 * @return bool
892
	 * @author Dmitry (dio) Levashov
893
	 **/
894
	protected function _filePutContents($path, $content) {
895
		if (@file_put_contents($path, $content, LOCK_EX) !== false) {
896
			clearstatcache();
897
			return true;
898
		}
899
		return false;
900
	}
901
902
	/**
903
	 * Detect available archivers
904
	 *
905
	 * @return void
906
	 **/
907
	protected function _checkArchivers() {
908
		$this->archivers = $this->getArchivers();
909
		return;
910
	}
911
912
	/**
913
	 * chmod availability
914
	 *
915
	 * @return bool
916
	 **/
917
	protected function _chmod($path, $mode) {
918
		$modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o",$mode));
919
		$ret = @chmod($path, $modeOct);
920
		$ret && clearstatcache();
921
		return  $ret;
922
	}
923
924
	/**
925
	 * Recursive symlinks search
926
	 *
927
	 * @param  string  $path  file/dir path
928
	 * @return bool
929
	 * @author Dmitry (dio) Levashov
930
	 **/
931
	protected function _findSymlinks($path) {
932
		if (is_link($path)) {
933
			return true;
934
		}
935
		
936
		if (is_dir($path)) {
937
			foreach (scandir($path) as $name) {
938
				if ($name != '.' && $name != '..') {
939
					$p = $path.DIRECTORY_SEPARATOR.$name;
940
					if (is_link($p) || !$this->nameAccepted($name)
941
						||
942
					(($mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name)) && $mimeByName !== 'unknown' && !$this->allowPutMime($mimeByName))) {
943
						$this->setError(elFinder::ERROR_SAVE, $name);
944
						return true;
945
					}
946
					if (is_dir($p) && $this->_findSymlinks($p)) {
947
						return true;
948
					} elseif (is_file($p)) {
949
						$this->archiveSize += sprintf('%u', filesize($p));
950
					}
951
				}
952
			}
953
		} else {
954
			
955
			$this->archiveSize += sprintf('%u', filesize($path));
956
		}
957
		
958
		return false;
959
	}
960
961
	/**
962
	 * Extract files from archive
963
	 *
964
	 * @param  string  $path  archive path
965
	 * @param  array   $arc   archiver command and arguments (same as in $this->archivers)
966
	 * @return true
967
	 * @author Dmitry (dio) Levashov, 
968
	 * @author Alexey Sukhotin
969
	 **/
970
	protected function _extract($path, $arc) {
971
		
972
		if ($this->quarantine) {
973
974
			$dir     = $this->quarantine.DIRECTORY_SEPARATOR.md5(basename($path).mt_rand());
975
			$archive = $dir.DIRECTORY_SEPARATOR.basename($path);
976
			
977
			if (!@mkdir($dir)) {
978
				return false;
979
			}
980
			
981
			// insurance unexpected shutdown
982
			register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir));
983
			
984
			chmod($dir, 0777);
985
			
986
			// copy in quarantine
987
			if (!copy($path, $archive)) {
988
				return false;
989
			}
990
			
991
			// extract in quarantine
992
			$this->unpackArchive($archive, $arc);
993
			
994
			// get files list
995
			$ls = array();
996
			foreach (scandir($dir) as $i => $name) {
997
				if ($name != '.' && $name != '..') {
998
					$ls[] = $name;
999
				}
1000
			}
1001
			
1002
			// no files - extract error ?
1003
			if (empty($ls)) {
1004
				return false;
1005
			}
1006
			
1007
			$this->archiveSize = 0;
1008
			
1009
			// find symlinks
1010
			$symlinks = $this->_findSymlinks($dir);
1011
			
1012 View Code Duplication
			if ($symlinks) {
0 ignored issues
show
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...
1013
				$this->delTree($dir);
1014
				return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS)));
1015
			}
1016
1017
			// check max files size
1018 View Code Duplication
			if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) {
0 ignored issues
show
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...
1019
				$this->delTree($dir);
1020
				return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
1021
			}
1022
			
1023
			$extractTo = $this->extractToNewdir; // 'auto', ture or false
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% 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...
1024
			
1025
			// archive contains one item - extract in archive dir
1026
			$name = '';
1027
			$src = $dir.DIRECTORY_SEPARATOR.$ls[0];
1028
			if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $extractTo (integer) and 'auto' (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
1029
				$name = $ls[0];
1030
			} else if ($extractTo === 'auto' || $extractTo) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $extractTo (integer) and 'auto' (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
1031
				// for several files - create new directory
1032
				// create unique name for directory
1033
				$src = $dir;
1034
				$name = basename($path);
1035 View Code Duplication
				if (preg_match('/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/i', $name, $m)) {
0 ignored issues
show
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...
1036
					$name = substr($name, 0,  strlen($name)-strlen($m[0]));
1037
				}
1038
				$test = dirname($path).DIRECTORY_SEPARATOR.$name;
1039
				if (file_exists($test) || is_link($test)) {
1040
					$name = $this->uniqueName(dirname($path), $name, '-', false);
1041
				}
1042
			}
1043
			
1044
			if ($name !== '') {
1045
				$result  = dirname($path).DIRECTORY_SEPARATOR.$name;
1046
1047
				if (! @rename($src, $result)) {
1048
					$this->delTree($dir);
1049
					return false;
1050
				}
1051
			} else {
1052
				$dstDir = dirname($path);
1053
				$res = false;
0 ignored issues
show
$res 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...
1054
				$result = array();
1055
				foreach($ls as $name) {
1056
					$target = $dstDir.DIRECTORY_SEPARATOR.$name;
1057
					if (is_dir($target)) {
1058
						$this->delTree($target);
1059
					}
1060
					if (@rename($dir.DIRECTORY_SEPARATOR.$name, $target)) {
1061
						$result[] = $target;
1062
					}
1063
				}
1064
				if (!$result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1065
					$this->delTree($dir);
1066
					return false;
1067
				}
1068
			}
1069
			
1070
			is_dir($dir) && $this->delTree($dir);
1071
			
1072
			return (is_array($result) || file_exists($result)) ? $result : false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return is_array($result)...ult) ? $result : false; (array|string|false) is incompatible with the return type declared by the abstract method elFinderVolumeDriver::_extract of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1073
		}
1074
	}
1075
	
1076
	/**
1077
	 * Create archive and return its path
1078
	 *
1079
	 * @param  string  $dir    target dir
1080
	 * @param  array   $files  files names list
1081
	 * @param  string  $name   archive name
1082
	 * @param  array   $arc    archiver options
1083
	 * @return string|bool
1084
	 * @author Dmitry (dio) Levashov, 
1085
	 * @author Alexey Sukhotin
1086
	 **/
1087
	protected function _archive($dir, $files, $name, $arc) {
1088
		return $this->makeArchive($dir, $files, $name, $arc);
1089
	}
1090
	
1091
	/******************** Over write functions *************************/
1092
	
1093
	/**
1094
	 * File path of local server side work file path
1095
	 *
1096
	 * @param  string $path
1097
	 * @return string
1098
	 * @author Naoki Sawada
1099
	 */
1100
	protected function getWorkFile($path) {
1101
		return $path;
1102
	}
1103
1104
	/**
1105
	 * Delete dirctory trees
1106
	 *
1107
	 * @param string $localpath path need convert encoding to server encoding
1108
	 * @return boolean
1109
	 * @author Naoki Sawada
1110
	 */
1111
	protected function delTree($localpath) {
1112
		return $this->rmdirRecursive($localpath);
1113
	}
1114
1115
	/******************** Over write (Optimized) functions *************************/
1116
1117
	/**
1118
	 * Recursive files search
1119
	 *
1120
	 * @param  string  $path   dir path
1121
	 * @param  string  $q      search string
1122
	 * @param  array   $mimes
1123
	 * @return array
1124
	 * @author Dmitry (dio) Levashov
1125
	 * @author Naoki Sawada
1126
	 **/
1127
	protected function doSearch($path, $q, $mimes) {
1128
		if ($this->encoding) {
1129
			// non UTF-8 use elFinderVolumeDriver::doSearch()
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% 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...
1130
			return parent::doSearch($path, $q, $mimes);
1131
		}
1132
1133
		$this->doSearchCurrentQuery = $q;
1134
		$match = array();
1135
		try {
1136
			$iterator = new RecursiveIteratorIterator(
1137
				new RecursiveCallbackFilterIterator(
1138
					new RecursiveDirectoryIterator($path,
1139
						FilesystemIterator::KEY_AS_PATHNAME |
1140
						FilesystemIterator::SKIP_DOTS |
1141
						(defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS')?
1142
							RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0)
1143
					),
1144
					array($this, 'localFileSystemSearchIteratorFilter')
1145
				),
1146
				RecursiveIteratorIterator::SELF_FIRST,
1147
				RecursiveIteratorIterator::CATCH_GET_CHILD
1148
			);
1149
			foreach ($iterator as $key => $node) {
1150
				if ($node->isDir()) {
1151
					if ($this->stripos($node->getFilename(), $q) !== false) {
1152
						$match[] = $key;
1153
					}
1154
				} else {
1155
					$match[] = $key;
1156
				}
1157
			}
1158
		} catch (Exception $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1159
		
1160
		$result = array();
1161
		
1162
		if ($match) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $match of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1163
			foreach($match as $p) {
1164
				$stat = $this->stat($p);
1165
		
1166
				if (!$stat) { // invalid links
0 ignored issues
show
Bug Best Practice introduced by
The expression $stat of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1167
					continue;
1168
				}
1169
		
1170
				if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) {
1171
					continue;
1172
				}
1173
					
1174
				$name = $stat['name'];
0 ignored issues
show
$name 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...
1175
		
1176
				if ((!$mimes || $stat['mime'] !== 'directory')) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $mimes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1177
					$stat['path'] = $this->path($stat['hash']);
1178
					if ($this->URL && !isset($stat['url'])) {
1179
						$path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1));
1180
						$stat['url'] = $this->URL . $path;
1181
					}
1182
		
1183
					$result[] = $stat;
1184
				}
1185
			}
1186
		}
1187
		
1188
		return $result;
1189
	}
1190
1191
	/******************** Original local functions *************************/
1192
1193
	public function localFileSystemSearchIteratorFilter($file, $key, $iterator) {
1194
		if ($iterator->hasChildren()) {
1195
			return (bool)$this->attr($key, 'read', null, true);
1196
		}
1197
		return ($this->stripos($file->getFilename(), $this->doSearchCurrentQuery) === false)? false : true;
1198
	}
1199
	
1200
} // END class 
1201
1202