Completed
Push — 2.x ( 6ff22e...55a998 )
by Naoki
04:44
created

elFinder::info()   C

Complexity

Conditions 16
Paths 42

Size

Total Lines 53
Code Lines 40

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 53
rs 6.2752
cc 16
eloc 40
nc 42
nop 1

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * elFinder - file manager for web.
5
 * Core class.
6
 *
7
 * @package elfinder
8
 * @author Dmitry (dio) Levashov
9
 * @author Troex Nevelin
10
 * @author Alexey Sukhotin
11
 **/
12
class elFinder {
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...
13
	
14
	/**
15
	 * API version number
16
	 *
17
	 * @var string
18
	 **/
19
	protected $version = '2.0';
20
	
21
	/**
22
	 * Storages (root dirs)
23
	 *
24
	 * @var array
25
	 **/
26
	protected $volumes = array();
27
	
28
	/**
29
	 * Network mount drivers
30
	 * 
31
	 * @var array
32
	 */
33
	public static $netDrivers = array();
34
	
35
	/**
36
	 * elFinder global locale
37
	 * 
38
	 * @var string
39
	 */
40
	public static $locale = '';
41
	
42
	/**
43
	 * elFinder global sessionCacheKey
44
	 * 
45
	 * @var string
46
	 */
47
	public static $sessionCacheKey = '';
48
	
49
	/**
50
	 * elFinder base64encodeSessionData
51
	 * elFinder save session data as `UTF-8`
52
	 * If the session storage mechanism of the system does not allow `UTF-8`
53
	 * And it must be `true` option 'base64encodeSessionData' of elFinder
54
	 * 
55
	 * @var bool
56
	 */
57
	protected static $base64encodeSessionData = false;
58
	
59
	/**
60
	 * Session key of net mount volumes
61
	 * @var string
62
	 */
63
	protected $netVolumesSessionKey = '';
64
	
65
	/**
66
	 * Mounted volumes count
67
	 * Required to create unique volume id
68
	 *
69
	 * @var int
70
	 **/
71
	public static $volumesCnt = 1;
72
	
73
	/**
74
	 * Default root (storage)
75
	 *
76
	 * @var elFinderStorageDriver
77
	 **/
78
	protected $default = null;
79
	
80
	/**
81
	 * Commands and required arguments list
82
	 *
83
	 * @var array
84
	 **/
85
	protected $commands = array(
86
		'open'      => array('target' => false, 'tree' => false, 'init' => false, 'mimes' => false, 'compare' => false),
87
		'ls'        => array('target' => true, 'mimes' => false),
88
		'tree'      => array('target' => true),
89
		'parents'   => array('target' => true),
90
		'tmb'       => array('targets' => true),
91
		'file'      => array('target' => true, 'download' => false),
92
		'size'      => array('targets' => true),
93
		'mkdir'     => array('target' => true, 'name' => true),
94
		'mkfile'    => array('target' => true, 'name' => true, 'mimes' => false),
95
		'rm'        => array('targets' => true),
96
		'rename'    => array('target' => true, 'name' => true, 'mimes' => false),
97
		'duplicate' => array('targets' => true, 'suffix' => false),
98
		'paste'     => array('dst' => true, 'targets' => true, 'cut' => false, 'mimes' => false, 'renames' => false, 'suffix' => false),
99
		'upload'    => array('target' => true, 'FILES' => true, 'mimes' => false, 'html' => false, 'upload' => false, 'name' => false, 'upload_path' => false, 'chunk' => false, 'cid' => false, 'node' => false, 'renames' => false, 'suffix' => false),
100
		'get'       => array('target' => true, 'conv' => false),
101
		'put'       => array('target' => true, 'content' => '', 'mimes' => false),
102
		'archive'   => array('targets' => true, 'type' => true, 'mimes' => false, 'name' => false),
103
		'extract'   => array('target' => true, 'mimes' => false, 'makedir' => false),
104
		'search'    => array('q' => true, 'mimes' => false, 'target' => false),
105
		'info'      => array('targets' => true, 'compare' => false),
106
		'dim'       => array('target' => true),
107
		'resize'    => array('target' => true, 'width' => true, 'height' => true, 'mode' => false, 'x' => false, 'y' => false, 'degree' => false, 'quality' => false),
108
		'netmount'  => array('protocol' => true, 'host' => true, 'path' => false, 'port' => false, 'user' => false, 'pass' => false, 'alias' => false, 'options' => false),
109
		'url'       => array('target' => true, 'options' => false),
110
		'callback'  => array('node' => true, 'json' => false, 'bind' => false, 'done' => false),
111
		'chmod'     => array('targets' => true, 'mode' => true)
112
	);
113
	
114
	/**
115
	 * Plugins instance
116
	 *
117
	 * @var array
118
	 **/
119
	protected $plugins = array();
120
	
121
	/**
122
	 * Commands listeners
123
	 *
124
	 * @var array
125
	 **/
126
	protected $listeners = array();
127
	
128
	/**
129
	 * script work time for debug
130
	 *
131
	 * @var string
132
	 **/
133
	protected $time = 0;
134
	/**
135
	 * Is elFinder init correctly?
136
	 *
137
	 * @var bool
138
	 **/
139
	protected $loaded = false;
140
	/**
141
	 * Send debug to client?
142
	 *
143
	 * @var string
144
	 **/
145
	protected $debug = false;
146
	
147
	/**
148
	 * Call `session_write_close()` before exec command?
149
	 * 
150
	 * @var bool
151
	 */
152
	protected $sessionCloseEarlier = true;
153
154
	/**
155
	 * SESSION use commands @see __construct()
156
	 * 
157
	 * @var array
158
	 */
159
	protected $sessionUseCmds = array();
160
	
161
	/**
162
	 * session expires timeout
163
	 *
164
	 * @var int
165
	 **/
166
	protected $timeout = 0;
167
	
168
	/**
169
	 * Temp dir path for Upload
170
	 * 
171
	 * @var string
172
	 */
173
	protected $uploadTempPath = '';
174
	
175
	/**
176
	 * undocumented class variable
177
	 *
178
	 * @var string
179
	 **/
180
	protected $uploadDebug = '';
181
	
182
	/**
183
	 * Errors from not mounted volumes
184
	 *
185
	 * @var array
186
	 **/
187
	public $mountErrors = array();
188
	
189
	/**
190
	 * URL for callback output window for CORS
191
	 * redirect to this URL when callback output
192
	 * 
193
	 * @var string URL
194
	 */
195
	protected $callbackWindowURL = '';
196
	
197
	// Errors messages
198
	const ERROR_UNKNOWN           = 'errUnknown';
199
	const ERROR_UNKNOWN_CMD       = 'errUnknownCmd';
200
	const ERROR_CONF              = 'errConf';
201
	const ERROR_CONF_NO_JSON      = 'errJSON';
202
	const ERROR_CONF_NO_VOL       = 'errNoVolumes';
203
	const ERROR_INV_PARAMS        = 'errCmdParams';
204
	const ERROR_OPEN              = 'errOpen';
205
	const ERROR_DIR_NOT_FOUND     = 'errFolderNotFound';
206
	const ERROR_FILE_NOT_FOUND    = 'errFileNotFound';     // 'File not found.'
207
	const ERROR_TRGDIR_NOT_FOUND  = 'errTrgFolderNotFound'; // 'Target folder "$1" not found.'
208
	const ERROR_NOT_DIR           = 'errNotFolder';
209
	const ERROR_NOT_FILE          = 'errNotFile';
210
	const ERROR_PERM_DENIED       = 'errPerm';
211
	const ERROR_LOCKED            = 'errLocked';        // '"$1" is locked and can not be renamed, moved or removed.'
212
	const ERROR_EXISTS            = 'errExists';        // 'File named "$1" already exists.'
213
	const ERROR_INVALID_NAME      = 'errInvName';       // 'Invalid file name.'
214
	const ERROR_MKDIR             = 'errMkdir';
215
	const ERROR_MKFILE            = 'errMkfile';
216
	const ERROR_RENAME            = 'errRename';
217
	const ERROR_COPY              = 'errCopy';
218
	const ERROR_MOVE              = 'errMove';
219
	const ERROR_COPY_FROM         = 'errCopyFrom';
220
	const ERROR_COPY_TO           = 'errCopyTo';
221
	const ERROR_COPY_ITSELF       = 'errCopyInItself';
222
	const ERROR_REPLACE           = 'errReplace';          // 'Unable to replace "$1".'
223
	const ERROR_RM                = 'errRm';               // 'Unable to remove "$1".'
224
	const ERROR_RM_SRC            = 'errRmSrc';            // 'Unable remove source file(s)'
225
	const ERROR_MKOUTLINK         = 'errMkOutLink';        // 'Unable to create a link to outside the volume root.'
226
	const ERROR_UPLOAD            = 'errUpload';           // 'Upload error.'
227
	const ERROR_UPLOAD_FILE       = 'errUploadFile';       // 'Unable to upload "$1".'
228
	const ERROR_UPLOAD_NO_FILES   = 'errUploadNoFiles';    // 'No files found for upload.'
229
	const ERROR_UPLOAD_TOTAL_SIZE = 'errUploadTotalSize';  // 'Data exceeds the maximum allowed size.'
230
	const ERROR_UPLOAD_FILE_SIZE  = 'errUploadFileSize';   // 'File exceeds maximum allowed size.'
231
	const ERROR_UPLOAD_FILE_MIME  = 'errUploadMime';       // 'File type not allowed.'
232
	const ERROR_UPLOAD_TRANSFER   = 'errUploadTransfer';   // '"$1" transfer error.'
233
	const ERROR_UPLOAD_TEMP       = 'errUploadTemp';       // 'Unable to make temporary file for upload.'
234
	// const ERROR_ACCESS_DENIED     = 'errAccess';
235
	const ERROR_NOT_REPLACE       = 'errNotReplace';       // Object "$1" already exists at this location and can not be replaced with object of another type.
236
	const ERROR_SAVE              = 'errSave';
237
	const ERROR_EXTRACT           = 'errExtract';
238
	const ERROR_ARCHIVE           = 'errArchive';
239
	const ERROR_NOT_ARCHIVE       = 'errNoArchive';
240
	const ERROR_ARCHIVE_TYPE      = 'errArcType';
241
	const ERROR_ARC_SYMLINKS      = 'errArcSymlinks';
242
	const ERROR_ARC_MAXSIZE       = 'errArcMaxSize';
243
	const ERROR_RESIZE            = 'errResize';
244
	const ERROR_UNSUPPORT_TYPE    = 'errUsupportType';
245
	const ERROR_CONV_UTF8         = 'errConvUTF8';
246
	const ERROR_NOT_UTF8_CONTENT  = 'errNotUTF8Content';
247
	const ERROR_NETMOUNT          = 'errNetMount';
248
	const ERROR_NETUNMOUNT        = 'errNetUnMount';
249
	const ERROR_NETMOUNT_NO_DRIVER = 'errNetMountNoDriver';
250
	const ERROR_NETMOUNT_FAILED       = 'errNetMountFailed';
251
252
	const ERROR_SESSION_EXPIRES 	= 'errSessionExpires';
253
254
	const ERROR_CREATING_TEMP_DIR 	= 'errCreatingTempDir';
255
	const ERROR_FTP_DOWNLOAD_FILE 	= 'errFtpDownloadFile';
256
	const ERROR_FTP_UPLOAD_FILE 	= 'errFtpUploadFile';
257
	const ERROR_FTP_MKDIR 		= 'errFtpMkdir';
258
	const ERROR_ARCHIVE_EXEC 	= 'errArchiveExec';
259
	const ERROR_EXTRACT_EXEC 	= 'errExtractExec';
260
261
	/**
262
	 * Constructor
263
	 *
264
	 * @param  array  elFinder and roots configurations
265
	 * @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...
266
	 * @author Dmitry (dio) Levashov
267
	 **/
268
	public function __construct($opts) {
0 ignored issues
show
Coding Style introduced by
__construct uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
__construct uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
__construct uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
__construct uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
269
		// try session start | restart
270
		try {
271
			session_start();
272
		} catch (Exception $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
273
		
274
		if (isset($opts['sessionUseCmds']) && is_array($opts['sessionUseCmds'])) {
275
			$sessionUseCmds = array_merge($sessionUseCmds, $opts['sessionUseCmds']);
0 ignored issues
show
Bug introduced by
The variable $sessionUseCmds seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

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

Let’s take a look at a simple example:

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

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

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

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

Loading history...
276
		}
277
278
		// set self::$volumesCnt by HTTP header "X-elFinder-VolumesCntStart"
279
		if (isset($_SERVER['HTTP_X_ELFINDER_VOLUMESCNTSTART']) && ($volumesCntStart = intval($_SERVER['HTTP_X_ELFINDER_VOLUMESCNTSTART']))) {
280
			self::$volumesCnt = $volumesCntStart;
281
		}
282
		
283
		$this->time  = $this->utime();
0 ignored issues
show
Documentation Bug introduced by
The property $time was declared of type string, but $this->utime() is of type double. Maybe add a type cast?

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

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

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
284
		$this->debug = (isset($opts['debug']) && $opts['debug'] ? true : false);
0 ignored issues
show
Documentation Bug introduced by
The property $debug was declared of type string, but isset($opts['debug']) &&...'debug'] ? true : false is of type boolean. Maybe add a type cast?

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

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

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
285
		$this->sessionCloseEarlier = isset($opts['sessionCloseEarlier'])? (bool)$opts['sessionCloseEarlier'] : true;
286
		$this->sessionUseCmds = array_flip($sessionUseCmds);
0 ignored issues
show
Bug introduced by
The variable $sessionUseCmds 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...
287
		$this->timeout = (isset($opts['timeout']) ? $opts['timeout'] : 0);
288
		$this->uploadTempPath = (isset($opts['uploadTempPath']) ? $opts['uploadTempPath'] : '');
289
		$this->netVolumesSessionKey = !empty($opts['netVolumesSessionKey'])? $opts['netVolumesSessionKey'] : 'elFinderNetVolumes';
290
		$this->callbackWindowURL = (isset($opts['callbackWindowURL']) ? $opts['callbackWindowURL'] : '');
291
		self::$sessionCacheKey = !empty($opts['sessionCacheKey']) ? $opts['sessionCacheKey'] : 'elFinderCaches';
292
		
293
		// check session cache
294
		$_optsMD5 = md5(json_encode($opts['roots']));
295
		if (! isset($_SESSION[self::$sessionCacheKey]) || $_SESSION[self::$sessionCacheKey]['_optsMD5'] !== $_optsMD5) {
296
			$_SESSION[self::$sessionCacheKey] = array(
297
				'_optsMD5' => $_optsMD5
298
			);
299
		}
300
		self::$base64encodeSessionData = !empty($opts['base64encodeSessionData']);
301
		
302
		// setlocale and global locale regists to elFinder::locale
303
		self::$locale = !empty($opts['locale']) ? $opts['locale'] : 'en_US.UTF-8';
304
		if (false === @setlocale(LC_ALL, self::$locale)) {
305
			self::$locale = setlocale(LC_ALL, '');
306
		}
307
308
		// bind events listeners
309
		if (!empty($opts['bind']) && is_array($opts['bind'])) {
310
			$_req = $_SERVER["REQUEST_METHOD"] == 'POST' ? $_POST : $_GET;
311
			$_reqCmd = isset($_req['cmd']) ? $_req['cmd'] : '';
312
			foreach ($opts['bind'] as $cmd => $handlers) {
313
				$doRegist = (strpos($cmd, '*') !== false);
314
				if (! $doRegist) {
315
					$_getcmd = create_function('$cmd', 'list($ret) = explode(\'.\', $cmd);return trim($ret);');
0 ignored issues
show
Security Best Practice introduced by
The use of create_function is highly discouraged, better use a closure.

create_function can pose a great security vulnerability as it is similar to eval, and could be used for arbitrary code execution. We highly recommend to use a closure instead.

// Instead of
$function = create_function('$a, $b', 'return $a + $b');

// Better use
$function = function($a, $b) { return $a + $b; }
Loading history...
316
					$doRegist = ($_reqCmd && in_array($_reqCmd, array_map($_getcmd, explode(' ', $cmd))));
317
				}
318
				if ($doRegist) {
319
					if (! is_array($handlers) || is_object($handlers[0])) {
320
						$handlers = array($handlers);
321
					}
322
					foreach($handlers as $handler) {
323
						if ($handler) {
324
							if (is_string($handler) && strpos($handler, '.')) {
325
								list($_domain, $_name, $_method) = array_pad(explode('.', $handler), 3, '');
326
								if (strcasecmp($_domain, 'plugin') === 0) {
327
									if ($plugin = $this->getPluginInstance($_name, isset($opts['plugin'][$_name])? $opts['plugin'][$_name] : array())
328
											and method_exists($plugin, $_method)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
329
										$this->bind($cmd, array($plugin, $_method));
330
									}
331
								}
332
							} else {
333
								$this->bind($cmd, $handler);
334
							}
335
						}
336
					}
337
				}
338
			}
339
		}
340
341
		if (!isset($opts['roots']) || !is_array($opts['roots'])) {
342
			$opts['roots'] = array();
343
		}
344
345
		// check for net volumes stored in session
346
		foreach ($this->getNetVolumes() as $key => $root) {
347
			$opts['roots'][$key] = $root;
348
		}
349
350
		// "mount" volumes
351
		foreach ($opts['roots'] as $i => $o) {
352
			$class = 'elFinderVolume'.(isset($o['driver']) ? $o['driver'] : '');
353
354
			if (class_exists($class)) {
355
				$volume = new $class();
356
357
				try {
358
					if ($volume->mount($o)) {
359
						// unique volume id (ends on "_") - used as prefix to files hash
360
						$id = $volume->id();
361
						
362
						$this->volumes[$id] = $volume;
363
						if ((!$this->default || $volume->root() !== $volume->defaultPath()) && $volume->isReadable()) {
364
							$this->default = $this->volumes[$id]; 
365
						}
366
					} else {
367
						$this->removeNetVolume($i);
368
						$this->mountErrors[] = 'Driver "'.$class.'" : '.implode(' ', $volume->error());
369
					}
370
				} catch (Exception $e) {
371
					$this->removeNetVolume($i);
372
					$this->mountErrors[] = 'Driver "'.$class.'" : '.$e->getMessage();
373
				}
374
			} else {
375
				$this->mountErrors[] = 'Driver "'.$class.'" does not exists';
376
			}
377
		}
378
379
		// if at least one readable volume - ii desu >_<
380
		$this->loaded = !empty($this->default);
381
	}
382
	
383
	/**
384
	 * Return true if fm init correctly
385
	 *
386
	 * @return bool
387
	 * @author Dmitry (dio) Levashov
388
	 **/
389
	public function loaded() {
390
		return $this->loaded;
391
	}
392
	
393
	/**
394
	 * Return version (api) number
395
	 *
396
	 * @return string
397
	 * @author Dmitry (dio) Levashov
398
	 **/
399
	public function version() {
400
		return $this->version;
401
	}
402
	
403
	/**
404
	 * Add handler to elFinder command
405
	 *
406
	 * @param  string  command name
407
	 * @param  string|array  callback name or array(object, method)
408
	 * @return elFinder
409
	 * @author Dmitry (dio) Levashov
410
	 **/
411
	public function bind($cmd, $handler) {
412
		$allCmds = array_keys($this->commands);
413
		$cmds = array();
414
		foreach(explode(' ', $cmd) as $_cmd) {
415
			if ($_cmd !== '') {
416
				if ($all = strpos($_cmd, '*') !== false) {
0 ignored issues
show
Unused Code introduced by
$all 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...
417
					list(, $sub) = array_pad(explode('.', $_cmd), 2, '');
418
					if ($sub) {
419
						$sub = str_replace('\'', '\\\'', $sub);
420
						$addSub = create_function('$cmd', 'return $cmd . \'.\' . trim(\'' . $sub . '\');');
0 ignored issues
show
Security Best Practice introduced by
The use of create_function is highly discouraged, better use a closure.

create_function can pose a great security vulnerability as it is similar to eval, and could be used for arbitrary code execution. We highly recommend to use a closure instead.

// Instead of
$function = create_function('$a, $b', 'return $a + $b');

// Better use
$function = function($a, $b) { return $a + $b; }
Loading history...
421
						$cmds = array_merge($cmds, array_map($addSub, $allCmds));
422
					} else {
423
						$cmds = array_merge($cmds, $allCmds);
424
					}
425
				} else {
426
					$cmds[] = $_cmd;
427
				}
428
			}
429
		}
430
		$cmds = array_unique($cmds);
431
		
432
		foreach ($cmds as $cmd) {
433
			if (!isset($this->listeners[$cmd])) {
434
				$this->listeners[$cmd] = array();
435
			}
436
437
			if (is_callable($handler)) {
438
				$this->listeners[$cmd][] = $handler;
439
			}
440
		}
441
442
		return $this;
443
	}
444
	
445
	/**
446
	 * Remove event (command exec) handler
447
	 *
448
	 * @param  string  command name
449
	 * @param  string|array  callback name or array(object, method)
450
	 * @return elFinder
451
	 * @author Dmitry (dio) Levashov
452
	 **/
453
	public function unbind($cmd, $handler) {
454
		if (!empty($this->listeners[$cmd])) {
455
			foreach ($this->listeners[$cmd] as $i => $h) {
456
				if ($h === $handler) {
457
					unset($this->listeners[$cmd][$i]);
458
					return $this;
459
				}
460
			}
461
		}
462
		return $this;
463
	}
464
	
465
	/**
466
	 * Return true if command exists
467
	 *
468
	 * @param  string  command name
469
	 * @return bool
470
	 * @author Dmitry (dio) Levashov
471
	 **/
472
	public function commandExists($cmd) {
473
		return $this->loaded && isset($this->commands[$cmd]) && method_exists($this, $cmd);
474
	}
475
	
476
	/**
477
	 * Return root - file's owner (public func of volume())
478
	 *
479
	 * @param  string  file hash
480
	 * @return elFinderStorageDriver
481
	 * @author Naoki Sawada
482
	 */
483
	public function getVolume($hash) {
484
		return $this->volume($hash);
485
	}
486
	
487
	/**
488
	 * Return command required arguments info
489
	 *
490
	 * @param  string  command name
491
	 * @return array
492
	 * @author Dmitry (dio) Levashov
493
	 **/
494
	public function commandArgsList($cmd) {
495
		return $this->commandExists($cmd) ? $this->commands[$cmd] : array();
496
	}
497
498
	private function session_expires() {
0 ignored issues
show
Coding Style introduced by
session_expires uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
499
		
500
		if (!isset($_SESSION[self::$sessionCacheKey . ':LAST_ACTIVITY'])) {
501
			$_SESSION[self::$sessionCacheKey . ':LAST_ACTIVITY'] = time();
502
			return false;
503
		}
504
505
		if ( ($this->timeout > 0) && (time() - $_SESSION[self::$sessionCacheKey . ':LAST_ACTIVITY'] > $this->timeout) ) {
506
			return true;
507
		}
508
509
		$_SESSION[self::$sessionCacheKey . ':LAST_ACTIVITY'] = time();
510
		return false;	
511
	}
512
	
513
	/**
514
	 * Exec command and return result
515
	 *
516
	 * @param  string  $cmd  command name
517
	 * @param  array   $args command arguments
518
	 * @return array
519
	 * @author Dmitry (dio) Levashov
520
	 **/
521
	public function exec($cmd, $args) {
522
		
523
		if (!$this->loaded) {
524
			return array('error' => $this->error(self::ERROR_CONF, self::ERROR_CONF_NO_VOL));
525
		}
526
527
		if ($this->session_expires()) {
528
			return array('error' => $this->error(self::ERROR_SESSION_EXPIRES));
529
		}
530
		
531
		if (!$this->commandExists($cmd)) {
532
			return array('error' => $this->error(self::ERROR_UNKNOWN_CMD));
533
		}
534
		
535
		if (!empty($args['mimes']) && is_array($args['mimes'])) {
536
			foreach ($this->volumes as $id => $v) {
537
				$this->volumes[$id]->setMimesFilter($args['mimes']);
538
			}
539
		}
540
541
		// call pre handlers for this command
542
		$args['sessionCloseEarlier'] = isset($this->sessionUseCmds[$cmd])? false : $this->sessionCloseEarlier;
543
		if (!empty($this->listeners[$cmd.'.pre'])) {
544
			$volume = isset($args['target'])? $this->volume($args['target']) : false;
545 View Code Duplication
			foreach ($this->listeners[$cmd.'.pre'] as $handler) {
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...
546
				call_user_func_array($handler, array($cmd, &$args, $this, $volume));
547
			}
548
		}
549
		
550
		// unlock session data for multiple access
551
		$this->sessionCloseEarlier && $args['sessionCloseEarlier'] && session_id() && session_write_close();
552
		
553
		if (substr(PHP_OS,0,3) === 'WIN') {
554
			// set time out
555
			if (($_max_execution_time = ini_get('max_execution_time')) && $_max_execution_time < 300) {
556
				@set_time_limit(300);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
557
			}
558
		}
559
		
560
		$result = $this->$cmd($args);
561
		
562
		if (isset($result['removed'])) {
563
			foreach ($this->volumes as $volume) {
564
				$result['removed'] = array_merge($result['removed'], $volume->removed());
565
				$volume->resetRemoved();
566
			}
567
		}
568
		
569
		// call handlers for this command
570
		if (!empty($this->listeners[$cmd])) {
571
			foreach ($this->listeners[$cmd] as $handler) {
572
				if (call_user_func_array($handler,array($cmd,&$result,$args,$this))) {
573
					// handler return true to force sync client after command completed
574
					$result['sync'] = true;
575
				}
576
			}
577
		}
578
		
579
		// replace removed files info with removed files hashes
580
		if (!empty($result['removed'])) {
581
			$removed = array();
582
			foreach ($result['removed'] as $file) {
583
				$removed[] = $file['hash'];
584
			}
585
			$result['removed'] = array_unique($removed);
586
		}
587
		// remove hidden files and filter files by mimetypes
588
		if (!empty($result['added'])) {
589
			$result['added'] = $this->filter($result['added']);
590
		}
591
		// remove hidden files and filter files by mimetypes
592
		if (!empty($result['changed'])) {
593
			$result['changed'] = $this->filter($result['changed']);
594
		}
595
		
596
		if ($this->debug || !empty($args['debug'])) {
597
			$result['debug'] = array(
598
				'connector' => 'php', 
599
				'phpver'    => PHP_VERSION,
600
				'time'      => $this->utime() - $this->time,
601
				'memory'    => (function_exists('memory_get_peak_usage') ? ceil(memory_get_peak_usage()/1024).'Kb / ' : '').ceil(memory_get_usage()/1024).'Kb / '.ini_get('memory_limit'),
602
				'upload'    => $this->uploadDebug,
603
				'volumes'   => array(),
604
				'mountErrors' => $this->mountErrors
605
				);
606
			
607
			foreach ($this->volumes as $id => $volume) {
608
				$result['debug']['volumes'][] = $volume->debug();
609
			}
610
		}
611
		
612
		foreach ($this->volumes as $volume) {
613
			$volume->umount();
614
		}
615
		
616
		if (!empty($result['callback'])) {
617
			$result['callback']['json'] = json_encode($result);
618
			$this->callback($result['callback']);
619
		} else {
620
			return $result;
621
		}
622
	}
623
	
624
	/**
625
	 * Return file real path
626
	 *
627
	 * @param  string  $hash  file hash
628
	 * @return string
629
	 * @author Dmitry (dio) Levashov
630
	 **/
631
	public function realpath($hash)	{
632
		if (($volume = $this->volume($hash)) == false) {
633
			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 elFinder::realpath 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...
634
		}
635
		return $volume->realpath($hash);
636
	}
637
	
638
	/**
639
	 * Return network volumes config.
640
	 *
641
	 * @return array
642
	 * @author Dmitry (dio) Levashov
643
	 */
644
	protected function getNetVolumes() {
0 ignored issues
show
Coding Style introduced by
getNetVolumes uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
645
		if (isset($_SESSION[$this->netVolumesSessionKey])) {
646
			if ($data = elFinder::sessionDataDecode($_SESSION[$this->netVolumesSessionKey], 'array')) {
0 ignored issues
show
Documentation introduced by
'array' is of type string, but the function expects a boolean|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
647
				return $data;
648
			}
649
		}
650
		return array();
651
	}
652
653
	/**
654
	 * Save network volumes config.
655
	 *
656
	 * @param  array  $volumes  volumes config
657
	 * @return void
658
	 * @author Dmitry (dio) Levashov
659
	 */
660
	protected function saveNetVolumes($volumes) {
0 ignored issues
show
Coding Style introduced by
saveNetVolumes uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
661
		$_SESSION[$this->netVolumesSessionKey] = elFinder::sessionDataEncode($volumes);
662
	}
663
664
	/**
665
	 * Remove netmount volume
666
	 * 
667
	 * @param string $key  netvolume key
668
	 */
669
	protected function removeNetVolume($key) {
670
		$netVolumes = $this->getNetVolumes();
671
		if (is_string($key) && isset($netVolumes[$key])) {
672
			unset($netVolumes[$key]);
673
			$this->saveNetVolumes($netVolumes);
674
		}
675
	}
676
677
	/**
678
	 * Get plugin instance & set to $this->plugins
679
	 * 
680
	 * @param  string $name   Plugin name (dirctory name)
681
	 * @param  array  $opts   Plugin options (optional)
682
	 * @return object | bool Plugin object instance Or false
683
	 * @author Naoki Sawada
684
	 */
685
	protected function getPluginInstance($name, $opts = array()) {
686
		$key = strtolower($name);
687
		if (! isset($this->plugins[$key])) {
688
			$p_file = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . $name . DIRECTORY_SEPARATOR . 'plugin.php';
689
			if (is_file($p_file)) {
690
				require_once $p_file;
691
				$class = 'elFinderPlugin' . $name;
692
				$this->plugins[$key] = new $class($opts);
693
			} else {
694
				$this->plugins[$key] = false;
695
			}
696
		}
697
		return $this->plugins[$key];
698
	}
699
700
	/***************************************************************************/
701
	/*                                 commands                                */
702
	/***************************************************************************/
703
	
704
	/**
705
	 * Normalize error messages
706
	 *
707
	 * @return array
708
	 * @author Dmitry (dio) Levashov
709
	 **/
710
	public function error() {
711
		$errors = array();
712
713
		foreach (func_get_args() as $msg) {
714
			if (is_array($msg)) {
715
				$errors = array_merge($errors, $msg);
716
			} else {
717
				$errors[] = $msg;
718
			}
719
		}
720
		
721
		return count($errors) ? $errors : array(self::ERROR_UNKNOWN);
722
	}
723
	
724
	protected function netmount($args) {
725
		// try session restart
726
		try {
727
			session_start();
728
		} catch (Exception $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
729
		
730
		$options  = array();
731
		$protocol = $args['protocol'];
732
		
733
		if ($protocol === 'netunmount') {
734
			$key = $args['host'];
735
			$netVolumes = $this->getNetVolumes();
736
			if ($netVolumes[$key]) {
737
				$res = true;
738
				$volume = $this->volume($args['user']);
739
				if (method_exists($volume, 'netunmount')) {
740
					$res = $volume->netunmount($netVolumes, $key);
741
				}
742
				if ($res) {
743
					unset($netVolumes[$key]);
744
					$this->saveNetVolumes($netVolumes);
745
					return array('sync' => true);
746
				}
747
			}
748
			return array('error' => $this->error(self::ERROR_NETUNMOUNT));
749
		}
750
		
751
		$driver   = isset(self::$netDrivers[$protocol]) ? self::$netDrivers[$protocol] : '';
752
		$class    = 'elfindervolume'.$driver;
753
754
		if (!class_exists($class)) {
755
			return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], self::ERROR_NETMOUNT_NO_DRIVER));
756
		}
757
758
		if (!$args['path']) {
759
			$args['path'] = '/';
760
		}
761
762
		foreach ($args as $k => $v) {
763
			if ($k != 'options' && $k != 'protocol' && $v) {
764
				$options[$k] = $v;
765
			}
766
		}
767
768
		if (is_array($args['options'])) {
769
			foreach ($args['options'] as $key => $value) {
770
				$options[$key] = $value;
771
			}
772
		}
773
774
		$volume = new $class();
775
		
776
		if (method_exists($volume, 'netmountPrepare')) {
777
			$options = $volume->netmountPrepare($options);
778
			if (isset($options['exit'])) {
779
				if ($options['exit'] === 'callback') {
780
					$this->callback($options['out']);
781
				}
782
				return $options;
783
			}
784
		}
785
		
786
		$netVolumes = $this->getNetVolumes();
787
		if ($volume->mount($options)) {
788
			if (! $key = @ $volume->netMountKey) {
789
				$key = md5($protocol . '-' . join('-', $options));
790
			}
791
			$options['driver'] = $driver;
792
			$options['netkey'] = $key;
793
			$netVolumes[$key]  = $options;
794
			$this->saveNetVolumes($netVolumes);
795
			$rootstat = $volume->file($volume->root());
796
			$rootstat['netkey'] = $key;
797
			return array('added' => array($rootstat));
798
		} else {
799
			$this->removeNetVolume($volume);
0 ignored issues
show
Documentation introduced by
$volume is of type object, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
800
			return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], implode(' ', $volume->error())));
801
		}
802
803
	}
804
805
	/**
806
	 * "Open" directory
807
	 * Return array with following elements
808
	 *  - cwd          - opened dir info
809
	 *  - files        - opened dir content [and dirs tree if $args[tree]]
810
	 *  - api          - api version (if $args[init])
811
	 *  - uplMaxSize   - if $args[init]
812
	 *  - error        - on failed
813
	 *
814
	 * @param  array  command arguments
815
	 * @return array
816
	 * @author Dmitry (dio) Levashov
817
	 **/
818
	protected function open($args) {
819
		$target = $args['target'];
820
		$init   = !empty($args['init']);
821
		$tree   = !empty($args['tree']);
822
		$volume = $this->volume($target);
823
		$cwd    = $volume ? $volume->dir($target) : false;
824
		$hash   = $init ? 'default folder' : '#'.$target;
825
		$sleep  = 0;
0 ignored issues
show
Unused Code introduced by
$sleep 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...
826
		$compare = '';
827
828
		// on init request we can get invalid dir hash -
829
		// dir which can not be opened now, but remembered by client,
830
		// so open default dir
831
		if ((!$cwd || !$cwd['read']) && $init) {
832
			$volume = $this->default;
833
			$cwd    = $volume->dir($volume->defaultPath());
834
		}
835
		
836
		if (!$cwd) {
837
			return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_DIR_NOT_FOUND));
838
		}
839
		if (!$cwd['read']) {
840
			return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_PERM_DENIED));
841
		}
842
843
		$files = array();
844
845
		// get other volume root
846
		if ($tree) {
847
			foreach ($this->volumes as $id => $v) {
848
				$files[] = $v->file($v->root());
849
			}
850
		}
851
852
		// get current working directory files list and add to $files if not exists in it
853 View Code Duplication
		if (($ls = $volume->scandir($cwd['hash'])) === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
854
			return array('error' => $this->error(self::ERROR_OPEN, $cwd['name'], $volume->error()));
855
		}
856
		// long polling mode
857
		if ($args['compare']) {
858
			$sleep = max(1, (int)$volume->getOption('lsPlSleep'));
859
			$limit = max(1, (int)$volume->getOption('plStandby') / $sleep) + 1;
860
			$timelimit = ini_get('max_execution_time');
861
			$compare = $args['compare'];
0 ignored issues
show
Unused Code introduced by
$compare 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...
862
			do {
863
				$timelimit && @ set_time_limit($timelimit + $sleep);
864
				$_mtime = 0;
865
				foreach($ls as $_f) {
866
					$_mtime = max($_mtime, $_f['ts']);
867
				}
868
				$compare = strval(count($ls)).':'.strval($_mtime);
869
				if ($compare !== $args['compare']) {
870
					break;
871
				}
872
				if (--$limit) {
873
					sleep($sleep);
874
					$volume->clearstatcache();
875
					if (($ls = $volume->scandir($cwd['hash'])) === false) {
876
						break;
877
					}
878
				}
879
			} while($limit);
880 View Code Duplication
			if ($ls === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
881
				return array('error' => $this->error(self::ERROR_OPEN, $cwd['name'], $volume->error()));
882
			}
883
		}
884
		
885
		if ($ls) {
886
			if ($files) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $files 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...
887
				$files = array_merge($files, $ls);
888
				$files = array_unique($files, SORT_REGULAR);
889
			} else {
890
				$files = $ls;
891
			}
892
		}
893
		
894
		$result = array(
895
			'cwd'     => $cwd,
896
			'options' => $volume->options($cwd['hash']),
897
			'files'   => $files
898
		);
899
		
900
		if ($compare) {
901
			$result['cwd']['compare'] = $compare;
902
		}
903
		
904
		if (!empty($args['init'])) {
905
			$result['api'] = $this->version;
906
			$result['uplMaxSize'] = ini_get('upload_max_filesize');
907
			$result['uplMaxFile'] = ini_get('max_file_uploads');
908
			$result['netDrivers'] = array_keys(self::$netDrivers);
909
			if ($volume) {
910
				$result['cwd']['root'] = $volume->root();
911
			}
912
		}
913
		
914
		return $result;
915
	}
916
	
917
	/**
918
	 * Return dir files names list
919
	 *
920
	 * @param  array  command arguments
921
	 * @return array
922
	 * @author Dmitry (dio) Levashov
923
	 **/
924 View Code Duplication
	protected function ls($args) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
925
		$target = $args['target'];
926
		
927
		if (($volume = $this->volume($target)) == false
928
		|| ($list = $volume->ls($target)) === false) {
929
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
930
		}
931
		return array('list' => $list);
932
	}
933
	
934
	/**
935
	 * Return subdirs for required directory
936
	 *
937
	 * @param  array  command arguments
938
	 * @return array
939
	 * @author Dmitry (dio) Levashov
940
	 **/
941 View Code Duplication
	protected function tree($args) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
942
		$target = $args['target'];
943
		
944
		if (($volume = $this->volume($target)) == false
945
		|| ($tree = $volume->tree($target)) == false) {
946
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
947
		}
948
949
		return array('tree' => $tree);
950
	}
951
	
952
	/**
953
	 * Return parents dir for required directory
954
	 *
955
	 * @param  array  command arguments
956
	 * @return array
957
	 * @author Dmitry (dio) Levashov
958
	 **/
959 View Code Duplication
	protected function parents($args) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
960
		$target = $args['target'];
961
		
962
		if (($volume = $this->volume($target)) == false
963
		|| ($tree = $volume->parents($target)) == false) {
964
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
965
		}
966
967
		return array('tree' => $tree);
968
	}
969
	
970
	/**
971
	 * Return new created thumbnails list
972
	 *
973
	 * @param  array  command arguments
974
	 * @return array
975
	 * @author Dmitry (dio) Levashov
976
	 **/
977
	protected function tmb($args) {
978
		
979
		$result  = array('images' => array());
980
		$targets = $args['targets'];
981
		
982
		foreach ($targets as $target) {
983
			if (($volume = $this->volume($target)) != false
984
			&& (($tmb = $volume->tmb($target)) != false)) {
985
				$result['images'][$target] = $tmb;
986
			}
987
		}
988
		return $result;
989
	}
990
	
991
	/**
992
	 * Required to output file in browser when volume URL is not set 
993
	 * Return array contains opened file pointer, root itself and required headers
994
	 *
995
	 * @param  array  command arguments
996
	 * @return array
997
	 * @author Dmitry (dio) Levashov
998
	 **/
999
	protected function file($args) {
0 ignored issues
show
Coding Style introduced by
file uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1000
		$target   = $args['target'];
1001
		$download = !empty($args['download']);
1002
		$h403     = 'HTTP/1.x 403 Access Denied';
1003
		$h404     = 'HTTP/1.x 404 Not Found';
1004
1005 View Code Duplication
		if (($volume = $this->volume($target)) == false) { 
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1006
			return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
1007
		}
1008
		
1009 View Code Duplication
		if (($file = $volume->file($target)) == false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1010
			return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
1011
		}
1012
		
1013
		if (!$file['read']) {
1014
			return array('error' => 'Access denied', 'header' => $h403, 'raw' => true);
1015
		}
1016
		
1017 View Code Duplication
		if (($fp = $volume->open($target)) == false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1018
			return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
1019
		}
1020
1021
		// allow change MIME type by 'file.pre' callback functions
1022
		$mime = isset($args['mime'])? $args['mime'] : $file['mime'];
1023
		if ($download) {
1024
			$disp = 'attachment';
1025
		} else {
1026
			$dispInlineRegex = $volume->getOption('dispInlineRegex');
1027
			$inlineRegex = false;
1028
			if ($dispInlineRegex) {
1029
				$inlineRegex = '#' . str_replace('#', '\\#', $dispInlineRegex) . '#';
1030
				try {
1031
					preg_match($inlineRegex, '');
1032
				} catch(Exception $e) {
1033
					$inlineRegex = false;
1034
				}
1035
			}
1036
			if (!$inlineRegex) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $inlineRegex 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...
1037
				$inlineRegex = '#^(?:(?:image|text)|application/x-shockwave-flash$)#';
1038
			}
1039
			$disp  = preg_match($inlineRegex, $mime)? 'inline' : 'attachment';
1040
		}
1041
		
1042
		$filenameEncoded = rawurlencode($file['name']);
1043
		if (strpos($filenameEncoded, '%') === false) { // ASCII only
1044
			$filename = 'filename="'.$file['name'].'"';
1045
		} else {
1046
			$ua = $_SERVER['HTTP_USER_AGENT'];
1047
			if (preg_match('/MSIE [4-8]/', $ua)) { // IE < 9 do not support RFC 6266 (RFC 2231/RFC 5987)
1048
				$filename = 'filename="'.$filenameEncoded.'"';
1049
			} elseif (strpos($ua, 'Chrome') === false && strpos($ua, 'Safari') !== false && preg_match('#Version/[3-5]#', $ua)) { // Safari < 6
1050
				$filename = 'filename="'.str_replace('"', '', $file['name']).'"';
1051
			} else { // RFC 6266 (RFC 2231/RFC 5987)
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...
1052
				$filename = 'filename*=UTF-8\'\''.$filenameEncoded;
1053
			}
1054
		}
1055
		
1056
		$result = array(
1057
			'volume'  => $volume,
1058
			'pointer' => $fp,
1059
			'info'    => $file,
1060
			'header'  => array(
1061
				'Content-Type: '.$mime, 
1062
				'Content-Disposition: '.$disp.'; '.$filename,
1063
				'Content-Transfer-Encoding: binary',
1064
				'Content-Length: '.$file['size'],
1065
				'Connection: close'
1066
			)
1067
		);
1068
		if (isset($file['url']) && $file['url'] && $file['url'] != 1) {
1069
			$result['header'][] = 'Content-Location: '.$file['url'];
1070
		}
1071
		return $result;
1072
	}
1073
	
1074
	/**
1075
	 * Count total files size
1076
	 *
1077
	 * @param  array  command arguments
1078
	 * @return array
1079
	 * @author Dmitry (dio) Levashov
1080
	 **/
1081
	protected function size($args) {
1082
		$size = 0;
1083
		
1084
		foreach ($args['targets'] as $target) {
1085
			if (($volume = $this->volume($target)) == false
1086
			|| ($file = $volume->file($target)) == false
1087
			|| !$file['read']) {
1088
				return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
1089
			}
1090
			
1091
			$size += $volume->size($target);
1092
		}
1093
		return array('size' => $size);
1094
	}
1095
	
1096
	/**
1097
	 * Create directory
1098
	 *
1099
	 * @param  array  command arguments
1100
	 * @return array
1101
	 * @author Dmitry (dio) Levashov
1102
	 **/
1103 View Code Duplication
	protected function mkdir($args) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1104
		$target = $args['target'];
1105
		$name   = $args['name'];
1106
		
1107
		if (($volume = $this->volume($target)) == false) {
1108
			return array('error' => $this->error(self::ERROR_MKDIR, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target));
1109
		}
1110
1111
		return ($dir = $volume->mkdir($target, $name)) == false
1112
			? array('error' => $this->error(self::ERROR_MKDIR, $name, $volume->error()))
1113
			: array('added' => array($dir));
1114
	}
1115
	
1116
	/**
1117
	 * Create empty file
1118
	 *
1119
	 * @param  array  command arguments
1120
	 * @return array
1121
	 * @author Dmitry (dio) Levashov
1122
	 **/
1123 View Code Duplication
	protected function mkfile($args) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1124
		$target = $args['target'];
1125
		$name   = $args['name'];
1126
		
1127
		if (($volume = $this->volume($target)) == false) {
1128
			return array('error' => $this->error(self::ERROR_MKFILE, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target));
1129
		}
1130
1131
		return ($file = $volume->mkfile($target, $args['name'])) == false
1132
			? array('error' => $this->error(self::ERROR_MKFILE, $name, $volume->error()))
1133
			: array('added' => array($file));
1134
	}
1135
	
1136
	/**
1137
	 * Rename file
1138
	 *
1139
	 * @param  array  $args
1140
	 * @return array
1141
	 * @author Dmitry (dio) Levashov
1142
	 **/
1143
	protected function rename($args) {
1144
		$target = $args['target'];
1145
		$name   = $args['name'];
1146
		
1147 View Code Duplication
		if (($volume = $this->volume($target)) == false
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1148
		||  ($rm  = $volume->file($target)) == false) {
1149
			return array('error' => $this->error(self::ERROR_RENAME, '#'.$target, self::ERROR_FILE_NOT_FOUND));
1150
		}
1151
		$rm['realpath'] = $volume->realpath($target);
1152
		
1153
		return ($file = $volume->rename($target, $name)) == false
1154
			? array('error' => $this->error(self::ERROR_RENAME, $rm['name'], $volume->error()))
1155
			: array('added' => array($file), 'removed' => array($rm));
1156
	}
1157
	
1158
	/**
1159
	 * Duplicate file - create copy with "copy %d" suffix
1160
	 *
1161
	 * @param array  $args  command arguments
1162
	 * @return array
1163
	 * @author Dmitry (dio) Levashov
1164
	 **/
1165
	protected function duplicate($args) {
1166
		$targets = is_array($args['targets']) ? $args['targets'] : array();
1167
		$result  = array('added' => array());
1168
		$suffix  = empty($args['suffix']) ? 'copy' : $args['suffix'];
1169
		
1170
		foreach ($targets as $target) {
1171 View Code Duplication
			if (($volume = $this->volume($target)) == false
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1172
			|| ($src = $volume->file($target)) == false) {
1173
				$result['warning'] = $this->error(self::ERROR_COPY, '#'.$target, self::ERROR_FILE_NOT_FOUND);
1174
				break;
1175
			}
1176
			
1177
			if (($file = $volume->duplicate($target, $suffix)) == false) {
1178
				$result['warning'] = $this->error($volume->error());
1179
				break;
1180
			}
1181
			
1182
			$result['added'][] = $file;
1183
		}
1184
		
1185
		return $result;
1186
	}
1187
		
1188
	/**
1189
	 * Remove dirs/files
1190
	 *
1191
	 * @param array  command arguments
1192
	 * @return array
1193
	 * @author Dmitry (dio) Levashov
1194
	 **/
1195
	protected function rm($args) {
1196
		$targets = is_array($args['targets']) ? $args['targets'] : array();
1197
		$result  = array('removed' => array());
1198
		
1199
		foreach ($targets as $target) {
1200 View Code Duplication
			if (($volume = $this->volume($target)) == false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1201
				$result['warning'] = $this->error(self::ERROR_RM, '#'.$target, self::ERROR_FILE_NOT_FOUND);
1202
				return $result;
1203
			}
1204
			if (!$volume->rm($target)) {
1205
				$result['warning'] = $this->error($volume->error());
1206
				return $result;
1207
			}
1208
		}
1209
1210
		return $result;
1211
	}
1212
1213
	/**
1214
	* Get remote contents
1215
	*
1216
	* @param  string   $url     target url
1217
	* @param  int      $timeout timeout (sec)
1218
	* @param  int      $redirect_max redirect max count
1219
	* @param  string   $ua
1220
	* @param  resource $fp
1221
	* @return string or bool(false)
1222
	* @retval string contents
1223
	* @retval false  error
1224
	* @author Naoki Sawada
1225
	**/
1226
	protected function get_remote_contents( &$url, $timeout = 30, $redirect_max = 5, $ua = 'Mozilla/5.0', $fp = null ) {
1227
		$method = (function_exists('curl_exec') && !ini_get('safe_mode'))? 'curl_get_contents' : 'fsock_get_contents'; 
1228
		return $this->$method( $url, $timeout, $redirect_max, $ua, $fp );
1229
	}
1230
	
1231
	/**
1232
	 * Get remote contents with cURL
1233
	 *
1234
	 * @param  string   $url     target url
1235
	 * @param  int      $timeout timeout (sec)
1236
	 * @param  int      $redirect_max redirect max count
1237
	 * @param  string   $ua
1238
	 * @param  resource $outfp
1239
	 * @return string or bool(false)
1240
	 * @retval string contents
1241
	 * @retval false  error
1242
	 * @author Naoki Sawada
1243
	 **/
1244
	 protected function curl_get_contents( &$url, $timeout, $redirect_max, $ua, $outfp ){
1245
		$ch = curl_init();
1246
		curl_setopt( $ch, CURLOPT_URL, $url );
1247
		curl_setopt( $ch, CURLOPT_HEADER, false );
1248
		if ($outfp) {
1249
			curl_setopt( $ch, CURLOPT_FILE, $outfp );
1250
		} else {
1251
			curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
1252
			curl_setopt( $ch, CURLOPT_BINARYTRANSFER, true );
1253
		}
1254
		curl_setopt( $ch, CURLOPT_LOW_SPEED_LIMIT, 1 );
1255
		curl_setopt( $ch, CURLOPT_LOW_SPEED_TIME, $timeout );
1256
		curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
1257
		curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1);
1258
		curl_setopt( $ch, CURLOPT_MAXREDIRS, $redirect_max);
1259
		curl_setopt( $ch, CURLOPT_USERAGENT, $ua);
1260
		$result = curl_exec( $ch );
1261
		$url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
1262
		curl_close( $ch );
1263
		return $outfp? $outfp : $result;
1264
	}
1265
	
1266
	/**
1267
	 * Get remote contents with fsockopen()
1268
	 *
1269
	 * @param  string   $url          url
1270
	 * @param  int      $timeout      timeout (sec)
1271
	 * @param  int      $redirect_max redirect max count
1272
	 * @param  string   $ua
1273
	 * @param  resource $outfp
1274
	 * @return string or bool(false)
1275
	 * @retval string contents
1276
	 * @retval false  error
1277
	 * @author Naoki Sawada
1278
	 */
1279
	protected function fsock_get_contents( &$url, $timeout, $redirect_max, $ua, $outfp ) {
1280
1281
		$connect_timeout = 3;
1282
		$connect_try = 3;
1283
		$method = 'GET';
1284
		$readsize = 4096;
1285
1286
		$getSize = null;
1287
		$headers = '';
1288
		
1289
		$arr = parse_url($url);
1290
		if (!$arr){
1291
			// Bad request
1292
			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 elFinder::fsock_get_contents 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...
1293
		}
1294
		
1295
		// query
1296
		$arr['query'] = isset($arr['query']) ? '?'.$arr['query'] : '';
1297
		// port
1298
		$arr['port'] = isset($arr['port']) ? $arr['port'] : (!empty($arr['https'])? 443 : 80);
1299
		
1300
		$url_base = $arr['scheme'].'://'.$arr['host'].':'.$arr['port'];
1301
		$url_path = isset($arr['path']) ? $arr['path'] : '/';
1302
		$uri = $url_path.$arr['query'];
1303
		
1304
		$query = $method.' '.$uri." HTTP/1.0\r\n";
1305
		$query .= "Host: ".$arr['host']."\r\n";
1306
		if (!empty($ua)) $query .= "User-Agent: ".$ua."\r\n";
1307
		if (!is_null($getSize)) $query .= 'Range: bytes=0-' . ($getSize - 1) . "\r\n";
1308
		
1309
		$query .= $headers;
1310
1311
		$query .= "\r\n";
1312
1313
		$fp = $connect_try_count = 0;
1314
		while( !$fp && $connect_try_count < $connect_try ) {
1315
	
1316
			$errno = 0;
1317
			$errstr = "";
1318
			$fp = @ fsockopen(
1319
			$arr['https'].$arr['host'],
1320
			$arr['port'],
1321
			$errno,$errstr,$connect_timeout);
1322
			if ($fp) break;
1323
			$connect_try_count++;
1324
			if (connection_aborted()) {
1325
				exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method fsock_get_contents() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1326
			}
1327
			sleep(1); // wait 1sec
1328
		}
1329
		
1330
		$fwrite = 0;
0 ignored issues
show
Unused Code introduced by
$fwrite 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...
1331
		for ($written = 0; $written < strlen($query); $written += $fwrite) {
1332
			$fwrite = fwrite($fp, substr($query, $written));
1333
			if (!$fwrite) {
1334
				break;
1335
			}
1336
		}
1337
		
1338
		$response = '';
0 ignored issues
show
Unused Code introduced by
$response 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...
1339
		
1340
		if ($timeout) {
1341
			socket_set_timeout($fp, $timeout);
1342
		}
1343
		
1344
		$_response = '';
1345
		$header = '';
1346
		while($_response !== "\r\n"){
1347
			$_response = fgets($fp, $readsize);
1348
			$header .= $_response;
1349
		};
1350
		
1351
		$rccd = array_pad(explode(' ',$header,3), 3, ''); // array('HTTP/1.1','200','OK\r\n...')
0 ignored issues
show
Unused Code Comprehensibility introduced by
89% 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...
1352
		$rc = (int)$rccd[1];
1353
		
1354
		// Redirect
1355
		switch ($rc) {
1356
			case 307: // Temporary Redirect
1357
			case 303: // See Other
1358
			case 302: // Moved Temporarily
1359
			case 301: // Moved Permanently
1360
				$matches = array();
1361
				if (preg_match('/^Location: (.+?)(#.+)?$/im',$header,$matches) && --$redirect_max > 0) {
1362
					$url = trim($matches[1]);
1363
					$hash = isset($matches[2])? trim($matches[2]) : '';
0 ignored issues
show
Unused Code introduced by
$hash 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...
1364
					if (!preg_match('/^https?:\//',$url)) { // no scheme
1365
						if ($url{0} != '/') { // Relative path
1366
							// to Absolute path
1367
							$url = substr($url_path,0,strrpos($url_path,'/')).'/'.$url;
1368
						}
1369
						// add sheme,host
1370
						$url = $url_base.$url;
1371
					}
1372
					fclose($fp);
1373
					return $this->fsock_get_contents( $url, $timeout, $redirect_max, $ua, $outfp );
1374
				}
1375
		}
1376
		
1377
		$body = '';
1378
		if (!$outfp) {
1379
			$outfp = fopen('php://temp', 'rwb');
1380
			$body = true;
1381
		}
1382
		while(fwrite($outfp, fread($fp, $readsize))) {
1383
			if ($timeout) {
1384
				$_status = socket_get_status($fp);
1385
				if ($_status['timed_out']) {
1386
					fclose($outfp);
1387
					fclose($fp);
1388
					return false; // Request Time-out
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by elFinder::fsock_get_contents 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...
1389
				}
1390
			}
1391
		}
1392
		if ($body) {
1393
			rewind($outfp);
1394
			$body = stream_get_contents($outfp);
1395
			fclose($outfp);
1396
			$outfp = null;
1397
		}
1398
		
1399
		fclose($fp);
1400
		
1401
		return $outfp? $outfp : $body; // Data
0 ignored issues
show
Bug Compatibility introduced by
The expression $outfp ? $outfp : $body; of type boolean|string adds the type boolean to the return on line 1401 which is incompatible with the return type documented by elFinder::fsock_get_contents of type string.
Loading history...
1402
	}
1403
	
1404
	/**
1405
	 * Parse Data URI scheme
1406
	 * 
1407
	 * @param  string $str
1408
	 * @param  array  $extTable
1409
	 * @return array
1410
	 * @author Naoki Sawada
1411
	 */
1412
	protected function parse_data_scheme( $str, $extTable ) {
1413
		$data = $name = '';
1414
		if ($fp = fopen('data://'.substr($str, 5), 'rb')) {
1415 View Code Duplication
			if ($data = stream_get_contents($fp)) {
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...
1416
				$meta = stream_get_meta_data($fp);
1417
				$ext = isset($extTable[$meta['mediatype']])? '.' . $extTable[$meta['mediatype']] : '';
1418
				$name = substr(md5($data), 0, 8) . $ext;
1419
			}
1420
			fclose($fp);
1421
		}
1422
		return array($data, $name);
1423
	}
1424
	
1425
	/**
1426
	 * Detect file type extension by local path
1427
	 * 
1428
	 * @param  string $path Local path
1429
	 * @return string file type extension with dot
1430
	 * @author Naoki Sawada
1431
	 */
1432
	protected function detectFileExtension($path) {
1433
		static $type, $finfo, $extTable;
1434
		if (!$type) {
1435
			$keys = array_keys($this->volumes);
1436
			$volume = $this->volumes[$keys[0]];
1437
			$extTable = array_flip(array_unique($volume->getMimeTable()));
1438
			
1439
			if (class_exists('finfo', false)) {
1440
				$tmpFileInfo = @explode(';', @finfo_file(finfo_open(FILEINFO_MIME), __FILE__));
1441
			} else {
1442
				$tmpFileInfo = false;
1443
			}
1444
			$regexp = '/text\/x\-(php|c\+\+)/';
1445
			if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) {
1446
				$type = 'finfo';
1447
				$finfo = finfo_open(FILEINFO_MIME);
1448
			} elseif (function_exists('mime_content_type')
1449
					&& preg_match($regexp, array_shift(explode(';', mime_content_type(__FILE__))))) {
0 ignored issues
show
Bug introduced by
explode(';', mime_content_type(__FILE__)) cannot be passed to array_shift() as the parameter $array expects a reference.
Loading history...
1450
				$type = 'mime_content_type';
1451
			} elseif (function_exists('getimagesize')) {
1452
				$type = 'getimagesize';
1453
			} else {
1454
				$type = 'none';
1455
			}
1456
		}
1457
		
1458
		$mime = '';
1459
		if ($type === 'finfo') {
1460
			$mime = @finfo_file($finfo, $path);
1461
		} elseif ($type === 'mime_content_type') {
1462
			$mime = mime_content_type($path);
1463
		} elseif ($type === 'getimagesize') {
1464
			if ($img = @getimagesize($path)) {
1465
				$mime = $img['mime'];
1466
			}
1467
		}
1468
		
1469
		if ($mime) {
1470
			$mime = explode(';', $mime);
1471
			$mime = trim($mime[0]);
1472
			
1473 View Code Duplication
			if (in_array($mime, array('application/x-empty', 'inode/x-empty'))) {
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...
1474
				// finfo return this mime for empty files
1475
				$mime = 'text/plain';
1476
			} elseif ($mime == 'application/x-zip') {
1477
				// http://elrte.org/redmine/issues/163
1478
				$mime = 'application/zip';
1479
			}
1480
		}
1481
		
1482
		return ($mime && isset($extTable[$mime]))? ('.' . $extTable[$mime]) : '';
1483
	}
1484
	
1485
	/**
1486
	 * Get temporary dirctroy path
1487
	 * 
1488
	 * @param  string $volumeTempPath
1489
	 * @return string
1490
	 * @author Naoki Sawada
1491
	 */
1492
	private function getTempDir($volumeTempPath = null) {
1493
		$testDirs = array();
1494
		if ($this->uploadTempPath) {
1495
			$testDirs[] = rtrim(realpath($this->uploadTempPath), DIRECTORY_SEPARATOR);
1496
		}
1497
		if ($volumeTempPath) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $volumeTempPath of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null 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...
1498
			$testDirs[] = rtrim(realpath($volumeTempPath), DIRECTORY_SEPARATOR);
1499
		}
1500
		if (function_exists('sys_get_temp_dir')) {
1501
			$testDirs[] = sys_get_temp_dir();
1502
		}
1503
		$tempDir = '';
1504
		foreach($testDirs as $testDir) {
1505
			if (!$testDir || !is_dir($testDir)) continue;
1506
			if (is_writable($testDir)) {
1507
				$tempDir = $testDir;
1508
				$gc = time() - 3600;
1509
				foreach(glob($tempDir . DIRECTORY_SEPARATOR .'ELF*') as $cf) {
1510
					if (filemtime($cf) < $gc) {
1511
						@unlink($cf);
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...
1512
					}
1513
				}
1514
				break;
1515
			}
1516
		}
1517
		return $tempDir;
1518
	}
1519
	
1520
	/**
1521
	 * chmod
1522
	 *
1523
	 * @param array  command arguments
1524
	 * @return array
1525
	 * @author David Bartle
1526
	 **/
1527
	protected function chmod($args) {
1528
		$targets = $args['targets'];
1529
		$mode    = intval((string)$args['mode'], 8);
1530
1531
		if (!is_array($targets)) {
1532
			$targets = array($targets);
1533
		}
1534
		
1535
		$result = array();
1536
		
1537 View Code Duplication
		if (($volume = $this->volume($targets[0])) == false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1538
			$result['error'] = $this->error(self::ERROR_CONF_NO_VOL);
1539
			return $result;
1540
		}
1541
1542
		$files = array();
1543
		$errors = array();
1544
		foreach($targets as $target) {
1545
			$file = $volume->chmod($target, $mode);
1546
			if ($file) {
1547
				$files = array_merge($files, is_array($file)? $file : array($file));
1548
			} else {
1549
				$errors = array_merge($errors, $volume->error());
1550
			}
1551
		}
1552
		
1553
		if ($files) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $files 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...
1554
			$result['changed'] = $files;
1555
			if ($errors) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $errors 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...
1556
				$result['warning'] = $this->error($errors);
1557
			}
1558
		} else {
1559
			$result['error'] = $this->error($errors);
1560
		}
1561
		
1562
		return $result;
1563
	}
1564
	
1565
	/**
1566
	 * Check chunked upload files
1567
	 * 
1568
	 * @param string $tmpname  uploaded temporary file path
1569
	 * @param string $chunk    uploaded chunk file name
1570
	 * @param string $cid      uploaded chunked file id
1571
	 * @param string $tempDir  temporary dirctroy path
1572
	 * @return array (string JoinedTemporaryFilePath, string FileName) or (empty, empty)
1573
	 * @author Naoki Sawada
1574
	 */
1575
	private function checkChunkedFile($tmpname, $chunk, $cid, $tempDir, $volume = null) {
0 ignored issues
show
Coding Style introduced by
checkChunkedFile uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1576
		if (preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m)) {
1577
			$fname = $m[1];
1578
			$encname = md5($cid . '_' . $fname);
1579
			$base = $tempDir . DIRECTORY_SEPARATOR . 'ELF' . $encname;
1580
			$clast = intval($m[3]);
1581
			if (is_null($tmpname)) {
1582
				ignore_user_abort(true);
1583
				sleep(10); // wait 10 sec
1584
				// chunked file upload fail
1585
				foreach(glob($base . '*') as $cf) {
1586
					@unlink($cf);
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...
1587
				}
1588
				ignore_user_abort(false);
1589
				return;
1590
			}
1591
			
1592
			$range = isset($_POST['range'])? trim($_POST['range']) : '';
1593
			if ($range && preg_match('/^(\d+),(\d+),(\d+)$/', $range, $ranges)) {
1594
				$start = $ranges[1];
1595
				$len   = $ranges[2];
1596
				$size  = $ranges[3];
1597
				$tmp = $base . '.part';
1598
				$csize = filesize($tmpname);
1599
				
1600
				$tmpExists = is_file($tmp);
1601
				if (!$tmpExists) {
1602
					// check upload max size
1603
					$uploadMaxSize = $volume->getUploadMaxSize();
1604
					if ($uploadMaxSize > 0 && $size > $uploadMaxSize) {
1605
						return array(self::ERROR_UPLOAD_FILE_SIZE, false);
1606
					}
1607
					// make temp file
1608
					$ok = false;
1609
					if ($fp = fopen($tmp, 'wb')) {
1610
						flock($fp, LOCK_EX);
1611
						$ok = ftruncate($fp, $size);
1612
						flock($fp, LOCK_UN);
1613
						fclose($fp);
1614
						touch($base);
1615
					}
1616
					if (!$ok) {
1617
						unlink($tmp);
1618
						return array(self::ERROR_UPLOAD_TEMP, false);
1619
					}
1620
				} else {
1621
					// wait until makeing temp file (for anothor session)
1622
					$cnt = 1200; // Time limit 120 sec
1623
					while(!is_file($base) && --$cnt) {
1624
						usleep(100000); // wait 100ms
1625
					}
1626
					if (!$cnt) {
1627
						return array(self::ERROR_UPLOAD_TEMP, false);
1628
					}
1629
				}
1630
				
1631
				// check size info
1632
				if ($len != $csize || $start + $len > $size || ($tmpExists && $size != filesize($tmp))) {
1633
					return array(self::ERROR_UPLOAD_TEMP, false);
1634
				}
1635
				
1636
				// write chunk data
1637
				$writelen = 0;
0 ignored issues
show
Unused Code introduced by
$writelen 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...
1638
				$src = fopen($tmpname, 'rb');
1639
				$fp = fopen($tmp, 'cb');
1640
				fseek($fp, $start);
1641
				$writelen = stream_copy_to_stream($src, $fp, $len);
1642
				fclose($fp);
1643
				fclose($src);
1644
				if ($writelen != $len) {
1645
					return array(self::ERROR_UPLOAD_TEMP, false);
1646
				}
1647
				
1648
				// write counts
1649
				file_put_contents($base, "\0", FILE_APPEND | LOCK_EX);
1650
				
1651
				if (filesize($base) >= $clast + 1) {
1652
					// Completion
1653
					unlink($base);
1654
					return array($tmp, $fname);
1655
				}
1656
			} else {
1657
				// old way
1658
				$part = $base . $m[2];
1659
				if (move_uploaded_file($tmpname, $part)) {
1660
					@chmod($part, 0600);
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...
1661
					if ($clast < count(glob($base . '*'))) {
1662
						$parts = array();
1663
						for ($i = 0; $i <= $clast; $i++) {
1664
							$name = $base . '.' . $i . '_' . $clast;
1665
							if (is_readable($name)) {
1666
								$parts[] = $name;
1667
							} else {
1668
								$parts = null;
1669
								break;
1670
							}
1671
						}
1672
						if ($parts) {
1673
							if (!is_file($base)) {
1674
								touch($base);
1675
								if ($resfile = tempnam($tempDir, 'ELF')) {
1676
									$target = fopen($resfile, 'wb');
1677
									foreach($parts as $f) {
1678
										$fp = fopen($f, 'rb');
1679
										while (!feof($fp)) {
1680
											fwrite($target, fread($fp, 8192));
1681
										}
1682
										fclose($fp);
1683
										unlink($f);
1684
									}
1685
									fclose($target);
1686
									unlink($base);
1687
									return array($resfile, $fname);
1688
								}
1689
								unlink($base);
1690
							}
1691
						}
1692
					}
1693
				}
1694
			}
1695
		}
1696
		return array('', '');
1697
	}
1698
	
1699
	/**
1700
	 * Save uploaded files
1701
	 *
1702
	 * @param  array
1703
	 * @return array
1704
	 * @author Dmitry (dio) Levashov
1705
	 **/
1706
	protected function upload($args) {
0 ignored issues
show
Coding Style introduced by
upload uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
upload uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1707
		$ngReg  = '/[\/\\?*:|"<>]/';
1708
		$target = $args['target'];
1709
		$volume = $this->volume($target);
1710
		$files  = isset($args['FILES']['upload']) && is_array($args['FILES']['upload']) ? $args['FILES']['upload'] : array();
1711
		$header = empty($args['html']) ? array() : array('header' => 'Content-Type: text/html; charset=utf-8');
1712
		$result = array_merge(array('added' => array()), $header);
1713
		$paths  = $args['upload_path']? $args['upload_path'] : array();
1714
		$chunk  = $args['chunk']? $args['chunk'] : '';
1715
		$cid    = $args['cid']? (int)$args['cid'] : '';
1716
		
1717
		$renames= array();
1718
		$suffix = '~';
1719
		if ($args['renames'] && is_array($args['renames'])) {
1720
			$renames = array_flip($args['renames']);
1721 View Code Duplication
			if (is_string($args['suffix']) && ! preg_match($ngReg, $args['suffix'])) {
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...
1722
				$suffix = $args['suffix'];
1723
			}
1724
		}
1725
		
1726
		if (!$volume) {
1727
			return array_merge(array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target)), $header);
1728
		}
1729
		
1730
		// regist Shutdown function
1731
		$GLOBALS['elFinderTempFiles'] = array();
1732
// 		if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% 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...
1733
// 			$shutdownfunc = function(){ // <- Parse error on PHP < 5.3 ;-(
1734
// 				foreach(array_keys($GLOBALS['elFinderTempFiles']) as $f){
1735
// 					@unlink($f);
1736
// 				}
1737
// 			};
1738
// 		} else {
1739
			$shutdownfunc = create_function('', '
0 ignored issues
show
Security Best Practice introduced by
The use of create_function is highly discouraged, better use a closure.

create_function can pose a great security vulnerability as it is similar to eval, and could be used for arbitrary code execution. We highly recommend to use a closure instead.

// Instead of
$function = create_function('$a, $b', 'return $a + $b');

// Better use
$function = function($a, $b) { return $a + $b; }
Loading history...
1740
				foreach(array_keys($GLOBALS[\'elFinderTempFiles\']) as $f){
1741
					@unlink($f);
1742
				}
1743
			');
1744
//		}
1745
		register_shutdown_function($shutdownfunc);
1746
		
1747
		// file extentions table by MIME
1748
		$extTable = array_flip(array_unique($volume->getMimeTable()));
1749
		
1750
		if (empty($files)) {
1751
			if (!$args['upload'] && $args['name'] && is_array($args['name'])) {
1752
				$error = '';
1753
				$result['name'] = array();
1754
				foreach($args['name'] as $_i => $_name) {
1755
					if (!$volume->isUploadableByName($_name)) {
1756
						$error = $this->error(self::ERROR_UPLOAD_FILE, $_name, self::ERROR_UPLOAD_FILE_MIME);
1757
						break;
1758
					}
1759
					$result['name'][$_i] = preg_replace($ngReg, '_', $_name);
1760
				}
1761
				if ($error) {
1762
					$result['error'] = $error;
1763
					return $result;
1764
				}
1765
				$result = array_merge_recursive($result, $this->ls($args));
1766
				if (empty($result['list'])) {
1767
					$result['name'] = array();
1768
				} else {
1769
					$result['name'] = array_merge(array_intersect($result['name'], $result['list']));
1770
				}
1771
				return $result;
1772
			}
1773
			if (isset($args['upload']) && is_array($args['upload']) && ($tempDir = $this->getTempDir($volume->getTempPath()))) {
1774
				$names = array();
1775
				foreach($args['upload'] as $i => $url) {
1776
					// check chunked file upload commit
1777
					if ($args['chunk']) {
1778
						if ($url === 'chunkfail' && $args['mimes'] === 'chunkfail') {
1779
							$this->checkChunkedFile(null, $chunk, $cid, $tempDir);
1780
							if (preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m)) {
1781
								$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $m[1], self::ERROR_UPLOAD_TRANSFER);
1782
							}
1783
							return $result;
1784
						} else {
1785
							$tmpfname = $tempDir . '/' . $args['chunk'];
1786
							$files['tmp_name'][$i] = $tmpfname;
1787
							$files['name'][$i] = $url;
1788
							$files['error'][$i] = 0;
1789
							$GLOBALS['elFinderTempFiles'][$tmpfname] = true;
1790
							break;
1791
						}
1792
					}
1793
					
1794
					$tmpfname = $tempDir . DIRECTORY_SEPARATOR . 'ELF_FATCH_' . md5($url.microtime(true));
1795
					
1796
					$_name = '';
1797
					// check is data:
1798
					if (substr($url, 0, 5) === 'data:') {
1799
						list($data, $args['name'][$i]) = $this->parse_data_scheme($url, $extTable);
1800
					} else {
1801
						$fp = fopen($tmpfname, 'wb');
1802
						$data = $this->get_remote_contents($url, 30, 5, 'Mozilla/5.0', $fp);
1803
						$_POST['overwrite'] = false;
1804
						$_name = preg_replace('~^.*?([^/#?]+)(?:\?.*)?(?:#.*)?$~', '$1', rawurldecode($url));
1805
						// Check `Content-Disposition` response header
1806
						if ($data && ($headers = get_headers($url, true)) && !empty($headers['Content-Disposition'])) {
1807
							if (preg_match('/filename\*?=(?:(.+?)\'\')?"?([a-z0-9_.~%-]+)"?/i', $headers['Content-Disposition'], $m)) {
1808
								$_name = rawurldecode($m[2]);
1809
								if ($m[1] && strtoupper($m[1]) !== 'UTF-8' && function_exists('mb_convert_encoding')) {
1810
									$_name = mb_convert_encoding($_name, 'UTF-8', $m[1]);
1811
								}
1812
							}
1813
						}
1814
					}
1815
					if ($data) {
1816
						if (isset($args['name'][$i])) {
1817
							$_name = $args['name'][$i];
1818
						}
1819
						if ($_name) {
1820
							$_ext = '';
1821
							if (preg_match('/(\.[a-z0-9]{1,7})$/', $_name, $_match)) {
1822
								$_ext = $_match[1];
1823
							}
1824
							if ((is_resource($data) && fclose($data)) || file_put_contents($tmpfname, $data)) {
1825
								$GLOBALS['elFinderTempFiles'][$tmpfname] = true;
1826
								$_name = preg_replace($ngReg, '_', $_name);
1827
								list($_a, $_b) = array_pad(explode('.', $_name, 2), 2, '');
1828
								if ($_b === '') {
1829
									if ($_ext) {
1830
										rename($tmpfname, $tmpfname . $_ext);
1831
										$tmpfname = $tmpfname . $_ext;
1832
									}
1833
									$_b = $this->detectFileExtension($tmpfname);
1834
									$_name = $_a.$_b;
1835
								} else {
1836
									$_b = '.'.$_b;
1837
								}
1838
								if (isset($names[$_name])) {
1839
									$_name = $_a.'_'.$names[$_name]++.$_b;
1840
								} else {
1841
									$names[$_name] = 1;
1842
								}
1843
								$files['tmp_name'][$i] = $tmpfname;
1844
								$files['name'][$i] = $_name;
1845
								$files['error'][$i] = 0;
1846
							} else {
1847
								@ unlink($tmpfname);
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...
1848
							}
1849
						}
1850
					}
1851
				}
1852
			}
1853
			if (empty($files)) {
1854
				return array_merge(array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_UPLOAD_NO_FILES)), $header);
1855
			}
1856
		}
1857
		
1858
		foreach ($files['name'] as $i => $name) {
1859
			if (($error = $files['error'][$i]) > 0) {
1860
				$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $error == UPLOAD_ERR_INI_SIZE || $error == UPLOAD_ERR_FORM_SIZE ? self::ERROR_UPLOAD_FILE_SIZE : self::ERROR_UPLOAD_TRANSFER);
1861
				$this->uploadDebug = 'Upload error code: '.$error;
1862
				break;
1863
			}
1864
			
1865
			$tmpname = $files['tmp_name'][$i];
1866
			$path = ($paths && !empty($paths[$i]))? $paths[$i] : '';
1867
			if ($name === 'blob') {
1868
				if ($chunk) {
1869
					if ($tempDir = $this->getTempDir($volume->getTempPath())) {
1870
						list($tmpname, $name) = $this->checkChunkedFile($tmpname, $chunk, $cid, $tempDir, $volume);
1871
						if ($tmpname) {
1872
							if ($name === false) {
1873
								preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m);
1874
								$result['error'] = $this->error(self::ERROR_UPLOAD_FILE, $m[1], $tmpname);
1875
								$result['_chunkfailure'] = true;
1876
								$this->uploadDebug = 'Upload error: ' . $tmpname;
1877
							} else if ($name) {
1878
								$result['_chunkmerged'] = basename($tmpname);
1879
								$result['_name'] = $name;
1880
							}
1881
						}
1882
					} else {
1883
						$result['error'] = $this->error(self::ERROR_UPLOAD_FILE, $chunk, self::ERROR_UPLOAD_TRANSFER);
1884
						$this->uploadDebug = 'Upload error: unable open tmp file';
1885
					}
1886
					return $result;
1887 View Code Duplication
				} else {
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...
1888
					// for form clipboard with Google Chrome
1889
					$type = $files['type'][$i];
1890
					$ext = isset($extTable[$type])? '.' . $extTable[$type] : '';
1891
					$name = substr(md5(basename($tmpname)), 0, 8) . $ext;
1892
				}
1893
			}
1894
			
1895
			// do hook function 'upload.presave'
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% 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...
1896
			if (! empty($this->listeners['upload.presave'])) {
1897 View Code Duplication
				foreach($this->listeners['upload.presave'] as $handler) {
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...
1898
					call_user_func_array($handler, array(&$path, &$name, $tmpname, $this, $volume));
1899
				}
1900
			}
1901
			
1902
			if (($fp = fopen($tmpname, 'rb')) == false) {
1903
				$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, self::ERROR_UPLOAD_TRANSFER);
1904
				$this->uploadDebug = 'Upload error: unable open tmp file';
1905
				if (! is_uploaded_file($tmpname)) {
1906
					if (@ unlink($tmpname)) unset($GLOBALS['elFinderTempFiles'][$tmpfname]);
1907
					continue;
1908
				}
1909
				break;
1910
			}
1911
			$rnres = array();
1912
			if ($path) {
1913
				$_target = $volume->getUploadTaget($target, $path, $result);
1914
			} else {
1915
				$_target = $target;
1916
				// file rename for backup
1917
				if (isset($renames[$name])) {
1918
					$dir = $volume->realpath($_target);
1919
					$hash = $volume->getHash($dir, $name);
1920
					$rnres = $this->rename(array('target' => $hash, 'name' => $volume->uniqueName($dir, $name, $suffix, true, 0)));
1921
					if (!empty($rnres['error'])) {
1922
						$result['warning'] = $rnres['error'];
1923
						break;
1924
					}
1925
				}
1926
			}
1927
			if (! $_target || ($file = $volume->upload($fp, $_target, $name, $tmpname)) === false) {
1928
				$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $volume->error());
1929
				fclose($fp);
1930 View Code Duplication
				if (! is_uploaded_file($tmpname)) {
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...
1931
					if (@ unlink($tmpname)) unset($GLOBALS['elFinderTempFiles'][$tmpname]);;
1932
					continue;
1933
				}
1934
				break;
1935
			}
1936
			
1937
			is_resource($fp) && fclose($fp);
1938
			if (! is_uploaded_file($tmpname)){
1939
				clearstatcache();
1940 View Code Duplication
				if (!is_file($tmpname) || @ unlink($tmpname)) {
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...
1941
					unset($GLOBALS['elFinderTempFiles'][$tmpname]);
1942
				}
1943
			}
1944
			$result['added'][] = $file;
1945
			if ($rnres) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $rnres 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...
1946
				$result = array_merge_recursive($result, $rnres);
1947
			}
1948
		}
1949
		if ($GLOBALS['elFinderTempFiles']) {
1950
			foreach(array_keys($GLOBALS['elFinderTempFiles']) as $_temp) {
1951
				@ unlink($_temp);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1952
			}
1953
		}
1954
		$result['removed'] = $volume->removed();
1955
		
1956
		if (!empty($args['node'])) {
1957
			$result['callback'] = array(
1958
				'node' => $args['node'],
1959
				'bind' => 'upload'
1960
			);
1961
		}
1962
		return $result;
1963
	}
1964
		
1965
	/**
1966
	 * Copy/move files into new destination
1967
	 *
1968
	 * @param  array  command arguments
1969
	 * @return array
1970
	 * @author Dmitry (dio) Levashov
1971
	 **/
1972
	protected function paste($args) {
1973
		$dst     = $args['dst'];
1974
		$targets = is_array($args['targets']) ? $args['targets'] : array();
1975
		$cut     = !empty($args['cut']);
1976
		$error   = $cut ? self::ERROR_MOVE : self::ERROR_COPY;
1977
		$result  = array('added' => array(), 'removed' => array());
1978
		
1979
		if (($dstVolume = $this->volume($dst)) == false) {
1980
			return array('error' => $this->error($error, '#'.$targets[0], self::ERROR_TRGDIR_NOT_FOUND, '#'.$dst));
1981
		}
1982
		
1983
		$renames = array();
1984
		$suffix = '~';
1985
		if (!empty($args['renames'])) {
1986
			$renames = array_flip($args['renames']);
1987 View Code Duplication
			if (is_string($args['suffix']) && ! preg_match('/[\/\\?*:|"<>]/', $args['suffix'])) {
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...
1988
				$suffix = $args['suffix'];
1989
			}
1990
		}
1991
		
1992
		foreach ($targets as $target) {
1993 View Code Duplication
			if (($srcVolume = $this->volume($target)) == false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1994
				$result['warning'] = $this->error($error, '#'.$target, self::ERROR_FILE_NOT_FOUND);
1995
				break;
1996
			}
1997
			
1998
			$rnres = array();
1999
			if ($renames) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $renames 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...
2000
				$file = $srcVolume->file($target);
2001
				if (isset($renames[$file['name']])) {
2002
					$dir = $dstVolume->realpath($dst);
2003
					$hash = $dstVolume->getHash($dir, $file['name']);
2004
					$rnres = $this->rename(array('target' => $hash, 'name' => $dstVolume->uniqueName($dir, $file['name'], $suffix, true, 0)));
2005
					if (!empty($rnres['error'])) {
2006
						$result['warning'] = $rnres['error'];
2007
						break;
2008
					}
2009
				}
2010
			}
2011
			
2012
			if (($file = $dstVolume->paste($srcVolume, $target, $dst, $cut)) == false) {
2013
				$result['warning'] = $this->error($dstVolume->error());
2014
				break;
2015
			}
2016
			
2017
			$result['added'][] = $file;
2018
			if ($rnres) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $rnres 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...
2019
				$result = array_merge_recursive($result, $rnres);
2020
			}
2021
		}
2022
		return $result;
2023
	}
2024
	
2025
	/**
2026
	 * Return file content
2027
	 *
2028
	 * @param  array  $args  command arguments
2029
	 * @return array
2030
	 * @author Dmitry (dio) Levashov
2031
	 **/
2032
	protected function get($args) {
2033
		$target = $args['target'];
2034
		$volume = $this->volume($target);
2035
		
2036
		if (!$volume || ($file = $volume->file($target)) == false) {
2037
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target, self::ERROR_FILE_NOT_FOUND));
2038
		}
2039
		
2040 View Code Duplication
		if (($content = $volume->getContents($target)) === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2041
			return array('error' => $this->error(self::ERROR_OPEN, $volume->path($target), $volume->error()));
2042
		}
2043
		
2044
		if ($args['conv'] && function_exists('mb_detect_encoding') && function_exists('mb_convert_encoding')) {
2045
			$mime = isset($file['mime'])? $file['mime'] : '';
2046
			if ($mime && strtolower(substr($mime, 0, 4)) === 'text') {
2047
				if ($enc = mb_detect_encoding ( $content , mb_detect_order(), true)) {
2048
					if (strtolower($enc) !== 'utf-8') {
2049
						$content = mb_convert_encoding($content, 'UTF-8', $enc);
2050
					}
2051
				}
2052
			}
2053
		}
2054
		
2055
		$json = json_encode($content);
2056
2057
		if ($json === false || strlen($json) < strlen($content)) {
2058
			if ($args['conv']) {
2059
				return array('error' => $this->error(self::ERROR_CONV_UTF8,self::ERROR_NOT_UTF8_CONTENT, $volume->path($target)));
2060
			} else {
2061
				return array('doconv' => true);
2062
			}
2063
		}
2064
		
2065
		return array('content' => $content);
2066
	}
2067
	
2068
	/**
2069
	 * Save content into text file
2070
	 *
2071
	 * @return array
2072
	 * @author Dmitry (dio) Levashov
2073
	 **/
2074
	protected function put($args) {
2075
		$target = $args['target'];
2076
		
2077 View Code Duplication
		if (($volume = $this->volume($target)) == false
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2078
		|| ($file = $volume->file($target)) == false) {
2079
			return array('error' => $this->error(self::ERROR_SAVE, '#'.$target, self::ERROR_FILE_NOT_FOUND));
2080
		}
2081
		
2082 View Code Duplication
		if (($file = $volume->putContents($target, $args['content'])) == false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2083
			return array('error' => $this->error(self::ERROR_SAVE, $volume->path($target), $volume->error()));
2084
		}
2085
		
2086
		return array('changed' => array($file));
2087
	}
2088
2089
	/**
2090
	 * Extract files from archive
2091
	 *
2092
	 * @param  array  $args  command arguments
2093
	 * @return array
2094
	 * @author Dmitry (dio) Levashov, 
2095
	 * @author Alexey Sukhotin
2096
	 **/
2097
	protected function extract($args) {
2098
		$target = $args['target'];
2099
		$mimes  = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array();
0 ignored issues
show
Unused Code introduced by
$mimes 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...
2100
		$error  = array(self::ERROR_EXTRACT, '#'.$target);
0 ignored issues
show
Unused Code introduced by
$error 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...
2101
		$makedir = isset($args['makedir'])? (bool)$args['makedir'] : null;
2102
2103 View Code Duplication
		if (($volume = $this->volume($target)) == false
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2104
		|| ($file = $volume->file($target)) == false) {
2105
			return array('error' => $this->error(self::ERROR_EXTRACT, '#'.$target, self::ERROR_FILE_NOT_FOUND));
2106
		}  
2107
2108
		return ($file = $volume->extract($target, $makedir))
2109
			? array('added' => isset($file['read'])? array($file) : $file)
2110
			: array('error' => $this->error(self::ERROR_EXTRACT, $volume->path($target), $volume->error()));
2111
	}
2112
	
2113
	/**
2114
	 * Create archive
2115
	 *
2116
	 * @param  array  $args  command arguments
2117
	 * @return array
2118
	 * @author Dmitry (dio) Levashov, 
2119
	 * @author Alexey Sukhotin
2120
	 **/
2121
	protected function archive($args) {
2122
		$type    = $args['type'];
0 ignored issues
show
Unused Code introduced by
$type 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...
2123
		$targets = isset($args['targets']) && is_array($args['targets']) ? $args['targets'] : array();
2124
		$name    = isset($args['name'])? $args['name'] : '';
2125
	
2126 View Code Duplication
		if (($volume = $this->volume($targets[0])) == false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2127
			return $this->error(self::ERROR_ARCHIVE, self::ERROR_TRGDIR_NOT_FOUND);
2128
		}
2129
	
2130
		return ($file = $volume->archive($targets, $args['type'], $name))
2131
			? array('added' => array($file))
2132
			: array('error' => $this->error(self::ERROR_ARCHIVE, $volume->error()));
2133
	}
2134
	
2135
	/**
2136
	 * Search files
2137
	 *
2138
	 * @param  array  $args  command arguments
2139
	 * @return array
2140
	 * @author Dmitry Levashov
2141
	 **/
2142
	protected function search($args) {
2143
		$q      = trim($args['q']);
2144
		$mimes  = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array();
2145
		$target = !empty($args['target'])? $args['target'] : null;
2146
		$result = array();
2147
2148
		if (!is_null($target)) {
2149
			$volume = $this->volume($target);
2150
			$result = $volume->search($q, $mimes, $target);
2151
		} else {
2152
			foreach ($this->volumes as $volume) {
2153
				$result = array_merge($result, $volume->search($q, $mimes));
2154
			}
2155
		}
2156
		
2157
		return array('files' => $result);
2158
	}
2159
	
2160
	/**
2161
	 * Return file info (used by client "places" ui)
2162
	 *
2163
	 * @param  array  $args  command arguments
2164
	 * @return array
2165
	 * @author Dmitry Levashov
2166
	 **/
2167
	protected function info($args) {
2168
		$files = array();
2169
		$sleep = 0;
0 ignored issues
show
Unused Code introduced by
$sleep 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...
2170
		$compare = null;
2171
		// long polling mode
2172
		if ($args['compare'] && count($args['targets']) === 1) {
2173
			$compare = intval($args['compare']);
2174
			$hash = $args['targets'][0];
2175
			if ($volume = $this->volume($hash)) {
2176
				$standby = (int)$volume->getOption('plStandby');
2177
				$_compare = false;
2178
				if (($syncCheckFunc = $volume->getOption('syncCheckFunc')) && is_callable($syncCheckFunc)) {
2179
					$_compare = call_user_func_array($syncCheckFunc, array($volume->realpath($hash), $standby, $compare, $volume, $this));
2180
				}
2181
				if ($_compare !== false) {
2182
					$compare = $_compare;
2183
				} else {
2184
					$sleep = max(1, (int)$volume->getOption('tsPlSleep'));
2185
					$limit = max(1, $standby / $sleep) + 1;
2186
					$timelimit = ini_get('max_execution_time');
2187
					do {
2188
						$timelimit && @ set_time_limit($timelimit + $sleep);
2189
						$volume->clearstatcache();
2190
						if (($info = $volume->file($hash)) != false) {
2191
							if ($info['ts'] != $compare) {
2192
								$compare = $info['ts'];
2193
								break;
2194
							}
2195
						} else {
2196
							$compare = 0;
2197
							break;
2198
						}
2199
						if (--$limit) {
2200
							sleep($sleep);
2201
						}
2202
					} while($limit);
2203
				}
2204
			}
2205
		} else {
2206
			foreach ($args['targets'] as $hash) {
2207
				if (($volume = $this->volume($hash)) != false
2208
				&& ($info = $volume->file($hash)) != false) {
2209
					$files[] = $info;
2210
				}
2211
			}
2212
		}
2213
		
2214
		$result = array('files' => $files);
2215
		if (!is_null($compare)) {
2216
			$result['compare'] = strval($compare);
2217
		}
2218
		return $result;
2219
	}
2220
	
2221
	/**
2222
	 * Return image dimmensions
2223
	 *
2224
	 * @param  array  $args  command arguments
2225
	 * @return array
2226
	 * @author Dmitry (dio) Levashov
2227
	 **/
2228
	protected function dim($args) {
2229
		$target = $args['target'];
2230
		
2231
		if (($volume = $this->volume($target)) != false) {
2232
			$dim = $volume->dimensions($target);
2233
			return $dim ? array('dim' => $dim) : array();
2234
		}
2235
		return array();
2236
	}
2237
	
2238
	/**
2239
	 * Resize image
2240
	 *
2241
	 * @param  array  command arguments
2242
	 * @return array
2243
	 * @author Dmitry (dio) Levashov
2244
	 * @author Alexey Sukhotin
2245
	 **/
2246
	protected function resize($args) {
2247
		$target = $args['target'];
2248
		$width  = $args['width'];
2249
		$height = $args['height'];
2250
		$x      = (int)$args['x'];
2251
		$y      = (int)$args['y'];
2252
		$mode   = $args['mode'];
2253
		$bg     = null;
2254
		$degree = (int)$args['degree'];
2255
		$quality= (int)$args['quality'];
2256
		
2257 View Code Duplication
		if (($volume = $this->volume($target)) == false
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2258
		|| ($file = $volume->file($target)) == false) {
2259
			return array('error' => $this->error(self::ERROR_RESIZE, '#'.$target, self::ERROR_FILE_NOT_FOUND));
2260
		}
2261
2262
		return ($file = $volume->resize($target, $width, $height, $x, $y, $mode, $bg, $degree, $quality))
2263
			? array('changed' => array($file))
2264
			: array('error' => $this->error(self::ERROR_RESIZE, $volume->path($target), $volume->error()));
2265
	}
2266
	
2267
	/**
2268
	* Return content URL
2269
	*
2270
	* @param  array  $args  command arguments
2271
	* @return array
2272
	* @author Naoki Sawada
2273
	**/
2274
	protected function url($args) {
2275
		$target = $args['target'];
2276
		$options = isset($args['options'])? $args['options'] : array();
2277
		if (($volume = $this->volume($target)) != false) {
2278
			$url = $volume->getContentUrl($target, $options);
2279
			return $url ? array('url' => $url) : array();
2280
		}
2281
		return array();
2282
	}
2283
2284
	/**
2285
	 * Output callback result with JavaScript that control elFinder
2286
	 * or HTTP redirect to callbackWindowURL
2287
	 * 
2288
	 * @param  array  command arguments
2289
	 * @author Naoki Sawada
2290
	 */
2291
	protected function callback($args) {
2292
		$checkReg = '/[^a-zA-Z0-9;._-]/';
2293
		$node = (isset($args['node']) && !preg_match($checkReg, $args['node']))? $args['node'] : '';
2294
		$json = (isset($args['json']) && @json_decode($args['json']))? $args['json'] : '{}';
2295
		$bind  = (isset($args['bind']) && !preg_match($checkReg, $args['bind']))? $args['bind'] : '';
2296
		$done = (!empty($args['done']));
2297
		
2298
		while( ob_get_level() ) {
2299
			if (! ob_end_clean()) {
2300
				break;
2301
			}
2302
		}
2303
		
2304
		if ($done || ! $this->callbackWindowURL) {
2305
			$script = '';
2306
			if ($node) {
2307
				$script .= '
2308
					var w = window.opener || window.parent || window;
2309
					try {
2310
						var elf = w.document.getElementById(\''.$node.'\').elfinder;
2311
						if (elf) {
2312
							var data = '.$json.';
2313
							if (data.error) {
2314
								elf.error(data.error);
2315
							} else {
2316
								data.warning && elf.error(data.warning);
2317
								data.removed && data.removed.length && elf.remove(data);
2318
								data.added   && data.added.length   && elf.add(data);
2319
								data.changed && data.changed.length && elf.change(data);';
2320
				if ($bind) {
2321
					$script .= '
2322
								elf.trigger(\''.$bind.'\', data);';
2323
				}
2324
				$script .= '
2325
								data.sync && elf.sync();
2326
							}
2327
						}
2328
					} catch(e) {
2329
						// for CORS
2330
						w.postMessage && w.postMessage(JSON.stringify({bind:\''.$bind.'\',data:'.$json.'}), \'*\');
2331
					}';
2332
			}
2333
			$script .= 'window.close();';
2334
			
2335
			$out = '<!DOCTYPE html><html><head><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><script>'.$script.'</script></head><body><a href="#" onlick="window.close();return false;">Close this window</a></body></html>';
2336
			
2337
			header('Content-Type: text/html; charset=utf-8');
2338
			header('Content-Length: '.strlen($out));
2339
			header('Cache-Control: private');
2340
			header('Pragma: no-cache');
2341
			
2342
			echo $out;
2343
			
2344
		} else {
2345
			$url = $this->callbackWindowURL;
2346
			$url .= ((strpos($url, '?') === false)? '?' : '&')
2347
				 . '&node=' . rawurlencode($node)
2348
				 . (($json !== '{}')? ('&json=' . rawurlencode($json)) : '')
2349
				 . ($bind? ('&bind=' .  rawurlencode($bind)) : '')
2350
				 . '&done=1';
2351
			
2352
			header('Location: ' . $url);
2353
			
2354
		}
2355
		exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method callback() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
2356
	}
2357
2358
	/***************************************************************************/
2359
	/*                                   utils                                 */
2360
	/***************************************************************************/
2361
	
2362
	/**
2363
	 * Return root - file's owner
2364
	 *
2365
	 * @param  string  file hash
2366
	 * @return elFinderStorageDriver
2367
	 * @author Dmitry (dio) Levashov
2368
	 **/
2369
	protected function volume($hash) {
2370
		foreach ($this->volumes as $id => $v) {
2371
			if (strpos(''.$hash, $id) === 0) {
2372
				return $this->volumes[$id];
2373
			} 
2374
		}
2375
		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 elFinder::volume of type elFinderStorageDriver.

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...
2376
	}
2377
	
2378
	/**
2379
	 * Return files info array 
2380
	 *
2381
	 * @param  array  $data  one file info or files info
2382
	 * @return array
2383
	 * @author Dmitry (dio) Levashov
2384
	 **/
2385
	protected function toArray($data) {
2386
		return isset($data['hash']) || !is_array($data) ? array($data) : $data;
2387
	}
2388
	
2389
	/**
2390
	 * Return fils hashes list
2391
	 *
2392
	 * @param  array  $files  files info
2393
	 * @return array
2394
	 * @author Dmitry (dio) Levashov
2395
	 **/
2396
	protected function hashes($files) {
2397
		$ret = array();
2398
		foreach ($files as $file) {
2399
			$ret[] = $file['hash'];
2400
		}
2401
		return $ret;
2402
	}
2403
	
2404
	/**
2405
	 * Remove from files list hidden files and files with required mime types
2406
	 *
2407
	 * @param  array  $files  files info
2408
	 * @return array
2409
	 * @author Dmitry (dio) Levashov
2410
	 **/
2411
	protected function filter($files) {
2412
		foreach ($files as $i => $file) {
2413
			if (!empty($file['hidden']) || !$this->default->mimeAccepted($file['mime'])) {
2414
				unset($files[$i]);
2415
			}
2416
		}
2417
		return array_merge($files, array());
2418
	}
2419
	
2420
	protected function utime() {
2421
		$time = explode(" ", microtime());
2422
		return (double)$time[1] + (double)$time[0];
2423
	}
2424
	
2425
	
2426
	/***************************************************************************/
2427
	/*                           static  utils                                 */
2428
	/***************************************************************************/
2429
	
2430
	/**
2431
	 * Return Is Animation Gif
2432
	 * 
2433
	 * @param  string $path server local path of target image
2434
	 * @return bool
2435
	 */
2436
	public static function isAnimationGif($path) {
2437
		list($width, $height, $type, $attr) = getimagesize($path);
0 ignored issues
show
Unused Code introduced by
The assignment to $width is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
Unused Code introduced by
The assignment to $height is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
Unused Code introduced by
The assignment to $attr is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
2438
		switch ($type) {
2439
			case IMAGETYPE_GIF:
2440
				break;
2441
			default:
2442
				return false;
2443
		}
2444
	
2445
		$imgcnt = 0;
2446
		$fp = fopen($path, 'rb');
2447
		@fread($fp, 4);
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...
2448
		$c = @fread($fp,1);
2449
		if (ord($c) != 0x39) {  // GIF89a
2450
			return false;
2451
		}
2452
	
2453
		while (!feof($fp)) {
2454
			do {
2455
				$c = fread($fp, 1);
2456
			} while(ord($c) != 0x21 && !feof($fp));
2457
	
2458
			if (feof($fp)) {
2459
				break;
2460
			}
2461
	
2462
			$c2 = fread($fp,2);
2463
			if (bin2hex($c2) == "f904") {
2464
				$imgcnt++;
2465
			}
2466
	
2467
			if (feof($fp)) {
2468
				break;
2469
			}
2470
		}
2471
	
2472
		if ($imgcnt > 1) {
2473
			return true;
2474
		} else {
2475
			return false;
2476
		}
2477
	}
2478
2479
	/**
2480
	 * Return Is seekable stream resource
2481
	 * 
2482
	 * @param resource $resource
2483
	 * @return bool
2484
	 */
2485
	public static function isSeekableStream($resource) {
2486
		$metadata = stream_get_meta_data($resource);
2487
		return $metadata['seekable'];
2488
	}
2489
2490
	/**
2491
	 * serialize and base64_encode of session data (If needed)
2492
	 * 
2493
	 * @param  mixed $var  target variable
2494
	 * @author Naoki Sawada
2495
	 */
2496
	public static function sessionDataEncode($var) {
2497
		if (self::$base64encodeSessionData) {
2498
			$var = base64_encode(serialize($var));
2499
		}
2500
		return $var;
2501
	}
2502
	
2503
	/**
2504
	 * base64_decode and unserialize of session data  (If needed)
2505
	 * 
2506
	 * @param  mixed $var      target variable
2507
	 * @param  bool  $checkIs  data type for check (array|string|object|int)
2508
	 * @author Naoki Sawada
2509
	 */
2510
	public static function sessionDataDecode(&$var, $checkIs = null) {
2511
		if (self::$base64encodeSessionData) {
2512
			$data = @unserialize(@base64_decode($var));
2513
		} else {
2514
			$data = $var;
2515
		}
2516
		$chk = true;
2517
		if ($checkIs) {
2518
			switch ($checkIs) {
2519
				case 'array':
2520
					$chk = is_array($data);
2521
					break;
2522
				case 'string':
2523
					$chk = is_string($data);
2524
					break;
2525
				case 'object':
2526
					$chk = is_object($data);
2527
					break;
2528
				case 'int':
2529
					$chk = is_int($data);
2530
					break;
2531
			}
2532
		}
2533
		if (!$chk) {
2534
			unset($var);
2535
			return false;
2536
		}
2537
		return $data;
2538
	}
2539
} // END class
2540