Completed
Push — 2.x ( 5243ac...ccb6e2 )
by Naoki
03:52
created

elFinder   D

Complexity

Total Complexity 537

Size/Duplication

Total Lines 2523
Duplicated Lines 5.83 %

Coupling/Cohesion

Components 1
Dependencies 0
Metric Value
wmc 537
lcom 1
cbo 0
dl 147
loc 2523
rs 4.4103

58 Methods

Rating   Name   Duplication   Size   Complexity  
A loaded() 0 3 1
A version() 0 3 1
C bind() 0 33 8
A unbind() 0 11 4
A commandExists() 0 3 3
A getVolume() 0 3 1
A commandArgsList() 0 3 2
A session_expires() 0 14 4
F __construct() 0 113 47
F exec() 3 103 32
A realpath() 0 6 2
A getNetVolumes() 0 8 3
A saveNetVolumes() 0 3 1
A removeNetVolume() 0 7 3
A getPluginInstance() 0 14 3
A error() 0 13 4
D netmount() 0 75 19
F open() 6 98 24
A ls() 9 9 3
A tree() 10 10 3
A parents() 10 10 3
A tmb() 0 13 4
D file() 9 74 19
B size() 0 14 5
A mkdir() 12 12 3
A mkfile() 12 12 3
A rename() 4 14 4
C duplicate() 5 22 7
B rm() 4 17 5
A get_remote_contents() 0 4 3
A curl_get_contents() 0 21 3
F fsock_get_contents() 0 124 31
A parse_data_scheme() 5 12 4
F detectFileExtension() 7 52 17
D getTempDir() 0 27 10
C chmod() 4 37 8
D checkChunkedFile() 0 123 30
F upload() 19 258 80
C paste() 7 52 14
C get() 3 35 15
A put() 7 14 4
B extract() 4 15 8
B archive() 3 13 6
B search() 0 17 6
C info() 0 53 16
A dim() 0 9 3
A resize() 4 20 4
A url() 0 9 4
F callback() 0 66 16
A volume() 0 8 3
A toArray() 0 3 3
A hashes() 0 7 2
A filter() 0 8 4
A utime() 0 4 1
D isAnimationGif() 0 42 10
A isSeekableStream() 0 4 1
A sessionDataEncode() 0 6 2
C sessionDataDecode() 0 29 8

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like elFinder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use elFinder, and based on these observations, apply Extract Interface, too.

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 default is `netmount`, `netunmount` @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
		if (session_id() == '') {
270
			session_start();
271
		}
272
		$sessionUseCmds = array('netmount', 'netunmount');
273
		if (isset($opts['sessionUseCmds']) && is_array($opts['sessionUseCmds'])) {
274
			$sessionUseCmds = array_merge($sessionUseCmds, $opts['sessionUseCmds']);
275
		}
276
277
		// set self::$volumesCnt by HTTP header "X-elFinder-VolumesCntStart"
278
		if (isset($_SERVER['HTTP_X_ELFINDER_VOLUMESCNTSTART']) && ($volumesCntStart = intval($_SERVER['HTTP_X_ELFINDER_VOLUMESCNTSTART']))) {
279
			self::$volumesCnt = $volumesCntStart;
280
		}
281
		
282
		$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...
283
		$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...
284
		$this->sessionCloseEarlier = isset($opts['sessionCloseEarlier'])? (bool)$opts['sessionCloseEarlier'] : true;
285
		$this->sessionUseCmds = array_flip($sessionUseCmds);
286
		$this->timeout = (isset($opts['timeout']) ? $opts['timeout'] : 0);
287
		$this->uploadTempPath = (isset($opts['uploadTempPath']) ? $opts['uploadTempPath'] : '');
288
		$this->netVolumesSessionKey = !empty($opts['netVolumesSessionKey'])? $opts['netVolumesSessionKey'] : 'elFinderNetVolumes';
289
		$this->callbackWindowURL = (isset($opts['callbackWindowURL']) ? $opts['callbackWindowURL'] : '');
290
		self::$sessionCacheKey = !empty($opts['sessionCacheKey']) ? $opts['sessionCacheKey'] : 'elFinderCaches';
291
		
292
		// check session cache
293
		$_optsMD5 = md5(json_encode($opts['roots']));
294
		if (! isset($_SESSION[self::$sessionCacheKey]) || $_SESSION[self::$sessionCacheKey]['_optsMD5'] !== $_optsMD5) {
295
			$_SESSION[self::$sessionCacheKey] = array(
296
				'_optsMD5' => $_optsMD5
297
			);
298
		}
299
		self::$base64encodeSessionData = !empty($opts['base64encodeSessionData']);
300
		
301
		// setlocale and global locale regists to elFinder::locale
302
		self::$locale = !empty($opts['locale']) ? $opts['locale'] : 'en_US.UTF-8';
303
		if (false === @setlocale(LC_ALL, self::$locale)) {
304
			self::$locale = setlocale(LC_ALL, '');
305
		}
306
307
		// bind events listeners
308
		if (!empty($opts['bind']) && is_array($opts['bind'])) {
309
			$_req = $_SERVER["REQUEST_METHOD"] == 'POST' ? $_POST : $_GET;
310
			$_reqCmd = isset($_req['cmd']) ? $_req['cmd'] : '';
311
			foreach ($opts['bind'] as $cmd => $handlers) {
312
				$doRegist = (strpos($cmd, '*') !== false);
313
				if (! $doRegist) {
314
					$_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...
315
					$doRegist = ($_reqCmd && in_array($_reqCmd, array_map($_getcmd, explode(' ', $cmd))));
316
				}
317
				if ($doRegist) {
318
					if (! is_array($handlers) || is_object($handlers[0])) {
319
						$handlers = array($handlers);
320
					}
321
					foreach($handlers as $handler) {
322
						if ($handler) {
323
							if (is_string($handler) && strpos($handler, '.')) {
324
								list($_domain, $_name, $_method) = array_pad(explode('.', $handler), 3, '');
325
								if (strcasecmp($_domain, 'plugin') === 0) {
326
									if ($plugin = $this->getPluginInstance($_name, isset($opts['plugin'][$_name])? $opts['plugin'][$_name] : array())
327
											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...
328
										$this->bind($cmd, array($plugin, $_method));
329
									}
330
								}
331
							} else {
332
								$this->bind($cmd, $handler);
333
							}
334
						}
335
					}
336
				}
337
			}
338
		}
339
340
		if (!isset($opts['roots']) || !is_array($opts['roots'])) {
341
			$opts['roots'] = array();
342
		}
343
344
		// check for net volumes stored in session
345
		foreach ($this->getNetVolumes() as $key => $root) {
346
			$opts['roots'][$key] = $root;
347
		}
348
349
		// "mount" volumes
350
		foreach ($opts['roots'] as $i => $o) {
351
			$class = 'elFinderVolume'.(isset($o['driver']) ? $o['driver'] : '');
352
353
			if (class_exists($class)) {
354
				$volume = new $class();
355
356
				try {
357
					if ($volume->mount($o)) {
358
						// unique volume id (ends on "_") - used as prefix to files hash
359
						$id = $volume->id();
360
						
361
						$this->volumes[$id] = $volume;
362
						if ((!$this->default || $volume->root() !== $volume->defaultPath()) && $volume->isReadable()) {
363
							$this->default = $this->volumes[$id]; 
364
						}
365
					} else {
366
						$this->removeNetVolume($i);
367
						$this->mountErrors[] = 'Driver "'.$class.'" : '.implode(' ', $volume->error());
368
					}
369
				} catch (Exception $e) {
370
					$this->removeNetVolume($i);
371
					$this->mountErrors[] = 'Driver "'.$class.'" : '.$e->getMessage();
372
				}
373
			} else {
374
				$this->mountErrors[] = 'Driver "'.$class.'" does not exists';
375
			}
376
		}
377
378
		// if at least one readable volume - ii desu >_<
379
		$this->loaded = !empty($this->default);
380
	}
381
	
382
	/**
383
	 * Return true if fm init correctly
384
	 *
385
	 * @return bool
386
	 * @author Dmitry (dio) Levashov
387
	 **/
388
	public function loaded() {
389
		return $this->loaded;
390
	}
391
	
392
	/**
393
	 * Return version (api) number
394
	 *
395
	 * @return string
396
	 * @author Dmitry (dio) Levashov
397
	 **/
398
	public function version() {
399
		return $this->version;
400
	}
401
	
402
	/**
403
	 * Add handler to elFinder command
404
	 *
405
	 * @param  string  command name
406
	 * @param  string|array  callback name or array(object, method)
407
	 * @return elFinder
408
	 * @author Dmitry (dio) Levashov
409
	 **/
410
	public function bind($cmd, $handler) {
411
		$allCmds = array_keys($this->commands);
412
		$cmds = array();
413
		foreach(explode(' ', $cmd) as $_cmd) {
414
			if ($_cmd !== '') {
415
				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...
416
					list(, $sub) = array_pad(explode('.', $_cmd), 2, '');
417
					if ($sub) {
418
						$sub = str_replace('\'', '\\\'', $sub);
419
						$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...
420
						$cmds = array_merge($cmds, array_map($addSub, $allCmds));
421
					} else {
422
						$cmds = array_merge($cmds, $allCmds);
423
					}
424
				} else {
425
					$cmds[] = $_cmd;
426
				}
427
			}
428
		}
429
		$cmds = array_unique($cmds);
430
		
431
		foreach ($cmds as $cmd) {
432
			if (!isset($this->listeners[$cmd])) {
433
				$this->listeners[$cmd] = array();
434
			}
435
436
			if (is_callable($handler)) {
437
				$this->listeners[$cmd][] = $handler;
438
			}
439
		}
440
441
		return $this;
442
	}
443
	
444
	/**
445
	 * Remove event (command exec) handler
446
	 *
447
	 * @param  string  command name
448
	 * @param  string|array  callback name or array(object, method)
449
	 * @return elFinder
450
	 * @author Dmitry (dio) Levashov
451
	 **/
452
	public function unbind($cmd, $handler) {
453
		if (!empty($this->listeners[$cmd])) {
454
			foreach ($this->listeners[$cmd] as $i => $h) {
455
				if ($h === $handler) {
456
					unset($this->listeners[$cmd][$i]);
457
					return $this;
458
				}
459
			}
460
		}
461
		return $this;
462
	}
463
	
464
	/**
465
	 * Return true if command exists
466
	 *
467
	 * @param  string  command name
468
	 * @return bool
469
	 * @author Dmitry (dio) Levashov
470
	 **/
471
	public function commandExists($cmd) {
472
		return $this->loaded && isset($this->commands[$cmd]) && method_exists($this, $cmd);
473
	}
474
	
475
	/**
476
	 * Return root - file's owner (public func of volume())
477
	 *
478
	 * @param  string  file hash
479
	 * @return elFinderStorageDriver
480
	 * @author Naoki Sawada
481
	 */
482
	public function getVolume($hash) {
483
		return $this->volume($hash);
484
	}
485
	
486
	/**
487
	 * Return command required arguments info
488
	 *
489
	 * @param  string  command name
490
	 * @return array
491
	 * @author Dmitry (dio) Levashov
492
	 **/
493
	public function commandArgsList($cmd) {
494
		return $this->commandExists($cmd) ? $this->commands[$cmd] : array();
495
	}
496
497
	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...
498
		
499
		if (!isset($_SESSION[self::$sessionCacheKey . ':LAST_ACTIVITY'])) {
500
			$_SESSION[self::$sessionCacheKey . ':LAST_ACTIVITY'] = time();
501
			return false;
502
		}
503
504
		if ( ($this->timeout > 0) && (time() - $_SESSION[self::$sessionCacheKey . ':LAST_ACTIVITY'] > $this->timeout) ) {
505
			return true;
506
		}
507
508
		$_SESSION[self::$sessionCacheKey . ':LAST_ACTIVITY'] = time();
509
		return false;	
510
	}
511
	
512
	/**
513
	 * Exec command and return result
514
	 *
515
	 * @param  string  $cmd  command name
516
	 * @param  array   $args command arguments
517
	 * @return array
518
	 * @author Dmitry (dio) Levashov
519
	 **/
520
	public function exec($cmd, $args) {
521
		
522
		if (!$this->loaded) {
523
			return array('error' => $this->error(self::ERROR_CONF, self::ERROR_CONF_NO_VOL));
524
		}
525
526
		if ($this->session_expires()) {
527
			return array('error' => $this->error(self::ERROR_SESSION_EXPIRES));
528
		}
529
		
530
		if (!$this->commandExists($cmd)) {
531
			return array('error' => $this->error(self::ERROR_UNKNOWN_CMD));
532
		}
533
		
534
		if (!empty($args['mimes']) && is_array($args['mimes'])) {
535
			foreach ($this->volumes as $id => $v) {
536
				$this->volumes[$id]->setMimesFilter($args['mimes']);
537
			}
538
		}
539
540
		// call pre handlers for this command
541
		$args['sessionCloseEarlier'] = isset($this->sessionUseCmds[$cmd])? false : $this->sessionCloseEarlier;
542
		if (!empty($this->listeners[$cmd.'.pre'])) {
543
			$volume = isset($args['target'])? $this->volume($args['target']) : false;
544 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...
545
				call_user_func_array($handler, array($cmd, &$args, $this, $volume));
546
			}
547
		}
548
		
549
		// unlock session data for multiple access
550
		$this->sessionCloseEarlier && $args['sessionCloseEarlier'] && session_id() && session_write_close();
551
		unset($this->sessionCloseEarlier);
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
		$options  = array();
726
		$protocol = $args['protocol'];
727
		
728
		if ($protocol === 'netunmount') {
729
			$key = $args['host'];
730
			$netVolumes = $this->getNetVolumes();
731
			if ($netVolumes[$key]) {
732
				$res = true;
733
				$volume = $this->volume($args['user']);
734
				if (method_exists($volume, 'netunmount')) {
735
					$res = $volume->netunmount($netVolumes, $key);
736
				}
737
				if ($res) {
738
					unset($netVolumes[$key]);
739
					$this->saveNetVolumes($netVolumes);
740
					return array('sync' => true);
741
				}
742
			}
743
			return array('error' => $this->error(self::ERROR_NETUNMOUNT));
744
		}
745
		
746
		$driver   = isset(self::$netDrivers[$protocol]) ? self::$netDrivers[$protocol] : '';
747
		$class    = 'elfindervolume'.$driver;
748
749
		if (!class_exists($class)) {
750
			return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], self::ERROR_NETMOUNT_NO_DRIVER));
751
		}
752
753
		if (!$args['path']) {
754
			$args['path'] = '/';
755
		}
756
757
		foreach ($args as $k => $v) {
758
			if ($k != 'options' && $k != 'protocol' && $v) {
759
				$options[$k] = $v;
760
			}
761
		}
762
763
		if (is_array($args['options'])) {
764
			foreach ($args['options'] as $key => $value) {
765
				$options[$key] = $value;
766
			}
767
		}
768
769
		$volume = new $class();
770
		
771
		if (method_exists($volume, 'netmountPrepare')) {
772
			$options = $volume->netmountPrepare($options);
773
			if (isset($options['exit'])) {
774
				if ($options['exit'] === 'callback') {
775
					$this->callback($options['out']);
776
				}
777
				return $options;
778
			}
779
		}
780
		
781
		$netVolumes = $this->getNetVolumes();
782
		if ($volume->mount($options)) {
783
			if (! $key = @ $volume->netMountKey) {
784
				$key = md5($protocol . '-' . join('-', $options));
785
			}
786
			$options['driver'] = $driver;
787
			$options['netkey'] = $key;
788
			$netVolumes[$key]  = $options;
789
			$this->saveNetVolumes($netVolumes);
790
			$rootstat = $volume->file($volume->root());
791
			$rootstat['netkey'] = $key;
792
			return array('added' => array($rootstat));
793
		} else {
794
			$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...
795
			return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], implode(' ', $volume->error())));
796
		}
797
798
	}
799
800
	/**
801
	 * "Open" directory
802
	 * Return array with following elements
803
	 *  - cwd          - opened dir info
804
	 *  - files        - opened dir content [and dirs tree if $args[tree]]
805
	 *  - api          - api version (if $args[init])
806
	 *  - uplMaxSize   - if $args[init]
807
	 *  - error        - on failed
808
	 *
809
	 * @param  array  command arguments
810
	 * @return array
811
	 * @author Dmitry (dio) Levashov
812
	 **/
813
	protected function open($args) {
814
		$target = $args['target'];
815
		$init   = !empty($args['init']);
816
		$tree   = !empty($args['tree']);
817
		$volume = $this->volume($target);
818
		$cwd    = $volume ? $volume->dir($target) : false;
819
		$hash   = $init ? 'default folder' : '#'.$target;
820
		$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...
821
		$compare = '';
822
823
		// on init request we can get invalid dir hash -
824
		// dir which can not be opened now, but remembered by client,
825
		// so open default dir
826
		if ((!$cwd || !$cwd['read']) && $init) {
827
			$volume = $this->default;
828
			$cwd    = $volume->dir($volume->defaultPath());
829
		}
830
		
831
		if (!$cwd) {
832
			return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_DIR_NOT_FOUND));
833
		}
834
		if (!$cwd['read']) {
835
			return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_PERM_DENIED));
836
		}
837
838
		$files = array();
839
840
		// get folders trees
841
		if ($tree) {
842
			foreach ($this->volumes as $id => $v) {
843
				$files[] = $v->file($v->root());
844
			}
845
		}
846
847
		// get current working directory files list and add to $files if not exists in it
848 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...
849
			return array('error' => $this->error(self::ERROR_OPEN, $cwd['name'], $volume->error()));
850
		}
851
		// long polling mode
852
		if ($args['compare']) {
853
			$sleep = max(1, (int)$volume->getOption('lsPlSleep'));
854
			$limit = max(1, (int)$volume->getOption('plStandby') / $sleep);
855
			$timelimit = ini_get('max_execution_time');
856
			$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...
857
			do {
858
				$timelimit && @ set_time_limit($timelimit + $sleep);
859
				$_mtime = 0;
860
				foreach($ls as $_f) {
861
					$_mtime = max($_mtime, $_f['ts']);
862
				}
863
				$compare = strval(count($ls)).':'.strval($_mtime);
864
				if ($compare !== $args['compare']) {
865
					break;
866
				}
867
				if (--$limit) {
868
					sleep($sleep);
869
					$volume->clearstatcache();
870
					if (($ls = $volume->scandir($cwd['hash'])) === false) {
871
						break;
872
					}
873
				}
874
			} while($limit);
875 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...
876
				return array('error' => $this->error(self::ERROR_OPEN, $cwd['name'], $volume->error()));
877
			}
878
		}
879
		
880
		if ($ls) {
881
			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...
882
				$files = array_merge($files, $ls);
883
				$files = array_unique($files, SORT_REGULAR);
884
			} else {
885
				$files = $ls;
886
			}
887
		}
888
		
889
		$result = array(
890
			'cwd'     => $cwd,
891
			'options' => $volume->options($cwd['hash']),
892
			'files'   => $files
893
		);
894
		
895
		if ($compare) {
896
			$result['cwd']['compare'] = $compare;
897
		}
898
		
899
		if (!empty($args['init'])) {
900
			$result['api'] = $this->version;
901
			$result['uplMaxSize'] = ini_get('upload_max_filesize');
902
			$result['uplMaxFile'] = ini_get('max_file_uploads');
903
			$result['netDrivers'] = array_keys(self::$netDrivers);
904
			if ($volume) {
905
				$result['cwd']['root'] = $volume->root();
906
			}
907
		}
908
		
909
		return $result;
910
	}
911
	
912
	/**
913
	 * Return dir files names list
914
	 *
915
	 * @param  array  command arguments
916
	 * @return array
917
	 * @author Dmitry (dio) Levashov
918
	 **/
919 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...
920
		$target = $args['target'];
921
		
922
		if (($volume = $this->volume($target)) == false
923
		|| ($list = $volume->ls($target)) === false) {
924
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
925
		}
926
		return array('list' => $list);
927
	}
928
	
929
	/**
930
	 * Return subdirs for required directory
931
	 *
932
	 * @param  array  command arguments
933
	 * @return array
934
	 * @author Dmitry (dio) Levashov
935
	 **/
936 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...
937
		$target = $args['target'];
938
		
939
		if (($volume = $this->volume($target)) == false
940
		|| ($tree = $volume->tree($target)) == false) {
941
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
942
		}
943
944
		return array('tree' => $tree);
945
	}
946
	
947
	/**
948
	 * Return parents dir for required directory
949
	 *
950
	 * @param  array  command arguments
951
	 * @return array
952
	 * @author Dmitry (dio) Levashov
953
	 **/
954 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...
955
		$target = $args['target'];
956
		
957
		if (($volume = $this->volume($target)) == false
958
		|| ($tree = $volume->parents($target)) == false) {
959
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
960
		}
961
962
		return array('tree' => $tree);
963
	}
964
	
965
	/**
966
	 * Return new created thumbnails list
967
	 *
968
	 * @param  array  command arguments
969
	 * @return array
970
	 * @author Dmitry (dio) Levashov
971
	 **/
972
	protected function tmb($args) {
973
		
974
		$result  = array('images' => array());
975
		$targets = $args['targets'];
976
		
977
		foreach ($targets as $target) {
978
			if (($volume = $this->volume($target)) != false
979
			&& (($tmb = $volume->tmb($target)) != false)) {
980
				$result['images'][$target] = $tmb;
981
			}
982
		}
983
		return $result;
984
	}
985
	
986
	/**
987
	 * Required to output file in browser when volume URL is not set 
988
	 * Return array contains opened file pointer, root itself and required headers
989
	 *
990
	 * @param  array  command arguments
991
	 * @return array
992
	 * @author Dmitry (dio) Levashov
993
	 **/
994
	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...
995
		$target   = $args['target'];
996
		$download = !empty($args['download']);
997
		$h403     = 'HTTP/1.x 403 Access Denied';
998
		$h404     = 'HTTP/1.x 404 Not Found';
999
1000 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...
1001
			return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
1002
		}
1003
		
1004 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...
1005
			return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
1006
		}
1007
		
1008
		if (!$file['read']) {
1009
			return array('error' => 'Access denied', 'header' => $h403, 'raw' => true);
1010
		}
1011
		
1012 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...
1013
			return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
1014
		}
1015
1016
		// allow change MIME type by 'file.pre' callback functions
1017
		$mime = isset($args['mime'])? $args['mime'] : $file['mime'];
1018
		if ($download) {
1019
			$disp = 'attachment';
1020
		} else {
1021
			$dispInlineRegex = $volume->getOption('dispInlineRegex');
1022
			$inlineRegex = false;
1023
			if ($dispInlineRegex) {
1024
				$inlineRegex = '#' . str_replace('#', '\\#', $dispInlineRegex) . '#';
1025
				try {
1026
					preg_match($inlineRegex, '');
1027
				} catch(Exception $e) {
1028
					$inlineRegex = false;
1029
				}
1030
			}
1031
			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...
1032
				$inlineRegex = '#^(?:(?:image|text)|application/x-shockwave-flash$)#';
1033
			}
1034
			$disp  = preg_match($inlineRegex, $mime)? 'inline' : 'attachment';
1035
		}
1036
		
1037
		$filenameEncoded = rawurlencode($file['name']);
1038
		if (strpos($filenameEncoded, '%') === false) { // ASCII only
1039
			$filename = 'filename="'.$file['name'].'"';
1040
		} else {
1041
			$ua = $_SERVER['HTTP_USER_AGENT'];
1042
			if (preg_match('/MSIE [4-8]/', $ua)) { // IE < 9 do not support RFC 6266 (RFC 2231/RFC 5987)
1043
				$filename = 'filename="'.$filenameEncoded.'"';
1044
			} elseif (strpos($ua, 'Chrome') === false && strpos($ua, 'Safari') !== false && preg_match('#Version/[3-5]#', $ua)) { // Safari < 6
1045
				$filename = 'filename="'.str_replace('"', '', $file['name']).'"';
1046
			} 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...
1047
				$filename = 'filename*=UTF-8\'\''.$filenameEncoded;
1048
			}
1049
		}
1050
		
1051
		$result = array(
1052
			'volume'  => $volume,
1053
			'pointer' => $fp,
1054
			'info'    => $file,
1055
			'header'  => array(
1056
				'Content-Type: '.$mime, 
1057
				'Content-Disposition: '.$disp.'; '.$filename,
1058
				'Content-Transfer-Encoding: binary',
1059
				'Content-Length: '.$file['size'],
1060
				'Connection: close'
1061
			)
1062
		);
1063
		if (isset($file['url']) && $file['url'] && $file['url'] != 1) {
1064
			$result['header'][] = 'Content-Location: '.$file['url'];
1065
		}
1066
		return $result;
1067
	}
1068
	
1069
	/**
1070
	 * Count total files size
1071
	 *
1072
	 * @param  array  command arguments
1073
	 * @return array
1074
	 * @author Dmitry (dio) Levashov
1075
	 **/
1076
	protected function size($args) {
1077
		$size = 0;
1078
		
1079
		foreach ($args['targets'] as $target) {
1080
			if (($volume = $this->volume($target)) == false
1081
			|| ($file = $volume->file($target)) == false
1082
			|| !$file['read']) {
1083
				return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
1084
			}
1085
			
1086
			$size += $volume->size($target);
1087
		}
1088
		return array('size' => $size);
1089
	}
1090
	
1091
	/**
1092
	 * Create directory
1093
	 *
1094
	 * @param  array  command arguments
1095
	 * @return array
1096
	 * @author Dmitry (dio) Levashov
1097
	 **/
1098 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...
1099
		$target = $args['target'];
1100
		$name   = $args['name'];
1101
		
1102
		if (($volume = $this->volume($target)) == false) {
1103
			return array('error' => $this->error(self::ERROR_MKDIR, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target));
1104
		}
1105
1106
		return ($dir = $volume->mkdir($target, $name)) == false
1107
			? array('error' => $this->error(self::ERROR_MKDIR, $name, $volume->error()))
1108
			: array('added' => array($dir));
1109
	}
1110
	
1111
	/**
1112
	 * Create empty file
1113
	 *
1114
	 * @param  array  command arguments
1115
	 * @return array
1116
	 * @author Dmitry (dio) Levashov
1117
	 **/
1118 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...
1119
		$target = $args['target'];
1120
		$name   = $args['name'];
1121
		
1122
		if (($volume = $this->volume($target)) == false) {
1123
			return array('error' => $this->error(self::ERROR_MKFILE, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target));
1124
		}
1125
1126
		return ($file = $volume->mkfile($target, $args['name'])) == false
1127
			? array('error' => $this->error(self::ERROR_MKFILE, $name, $volume->error()))
1128
			: array('added' => array($file));
1129
	}
1130
	
1131
	/**
1132
	 * Rename file
1133
	 *
1134
	 * @param  array  $args
1135
	 * @return array
1136
	 * @author Dmitry (dio) Levashov
1137
	 **/
1138
	protected function rename($args) {
1139
		$target = $args['target'];
1140
		$name   = $args['name'];
1141
		
1142 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...
1143
		||  ($rm  = $volume->file($target)) == false) {
1144
			return array('error' => $this->error(self::ERROR_RENAME, '#'.$target, self::ERROR_FILE_NOT_FOUND));
1145
		}
1146
		$rm['realpath'] = $volume->realpath($target);
1147
		
1148
		return ($file = $volume->rename($target, $name)) == false
1149
			? array('error' => $this->error(self::ERROR_RENAME, $rm['name'], $volume->error()))
1150
			: array('added' => array($file), 'removed' => array($rm));
1151
	}
1152
	
1153
	/**
1154
	 * Duplicate file - create copy with "copy %d" suffix
1155
	 *
1156
	 * @param array  $args  command arguments
1157
	 * @return array
1158
	 * @author Dmitry (dio) Levashov
1159
	 **/
1160
	protected function duplicate($args) {
1161
		$targets = is_array($args['targets']) ? $args['targets'] : array();
1162
		$result  = array('added' => array());
1163
		$suffix  = empty($args['suffix']) ? 'copy' : $args['suffix'];
1164
		
1165
		foreach ($targets as $target) {
1166 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...
1167
			|| ($src = $volume->file($target)) == false) {
1168
				$result['warning'] = $this->error(self::ERROR_COPY, '#'.$target, self::ERROR_FILE_NOT_FOUND);
1169
				break;
1170
			}
1171
			
1172
			if (($file = $volume->duplicate($target, $suffix)) == false) {
1173
				$result['warning'] = $this->error($volume->error());
1174
				break;
1175
			}
1176
			
1177
			$result['added'][] = $file;
1178
		}
1179
		
1180
		return $result;
1181
	}
1182
		
1183
	/**
1184
	 * Remove dirs/files
1185
	 *
1186
	 * @param array  command arguments
1187
	 * @return array
1188
	 * @author Dmitry (dio) Levashov
1189
	 **/
1190
	protected function rm($args) {
1191
		$targets = is_array($args['targets']) ? $args['targets'] : array();
1192
		$result  = array('removed' => array());
1193
		
1194
		foreach ($targets as $target) {
1195 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...
1196
				$result['warning'] = $this->error(self::ERROR_RM, '#'.$target, self::ERROR_FILE_NOT_FOUND);
1197
				return $result;
1198
			}
1199
			if (!$volume->rm($target)) {
1200
				$result['warning'] = $this->error($volume->error());
1201
				return $result;
1202
			}
1203
		}
1204
1205
		return $result;
1206
	}
1207
1208
	/**
1209
	* Get remote contents
1210
	*
1211
	* @param  string   $url     target url
1212
	* @param  int      $timeout timeout (sec)
1213
	* @param  int      $redirect_max redirect max count
1214
	* @param  string   $ua
1215
	* @param  resource $fp
1216
	* @return string or bool(false)
1217
	* @retval string contents
1218
	* @retval false  error
1219
	* @author Naoki Sawada
1220
	**/
1221
	protected function get_remote_contents( &$url, $timeout = 30, $redirect_max = 5, $ua = 'Mozilla/5.0', $fp = null ) {
1222
		$method = (function_exists('curl_exec') && !ini_get('safe_mode'))? 'curl_get_contents' : 'fsock_get_contents'; 
1223
		return $this->$method( $url, $timeout, $redirect_max, $ua, $fp );
1224
	}
1225
	
1226
	/**
1227
	 * Get remote contents with cURL
1228
	 *
1229
	 * @param  string   $url     target url
1230
	 * @param  int      $timeout timeout (sec)
1231
	 * @param  int      $redirect_max redirect max count
1232
	 * @param  string   $ua
1233
	 * @param  resource $outfp
1234
	 * @return string or bool(false)
1235
	 * @retval string contents
1236
	 * @retval false  error
1237
	 * @author Naoki Sawada
1238
	 **/
1239
	 protected function curl_get_contents( &$url, $timeout, $redirect_max, $ua, $outfp ){
1240
		$ch = curl_init();
1241
		curl_setopt( $ch, CURLOPT_URL, $url );
1242
		curl_setopt( $ch, CURLOPT_HEADER, false );
1243
		if ($outfp) {
1244
			curl_setopt( $ch, CURLOPT_FILE, $outfp );
1245
		} else {
1246
			curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
1247
			curl_setopt( $ch, CURLOPT_BINARYTRANSFER, true );
1248
		}
1249
		curl_setopt( $ch, CURLOPT_LOW_SPEED_LIMIT, 1 );
1250
		curl_setopt( $ch, CURLOPT_LOW_SPEED_TIME, $timeout );
1251
		curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
1252
		curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1);
1253
		curl_setopt( $ch, CURLOPT_MAXREDIRS, $redirect_max);
1254
		curl_setopt( $ch, CURLOPT_USERAGENT, $ua);
1255
		$result = curl_exec( $ch );
1256
		$url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
1257
		curl_close( $ch );
1258
		return $outfp? $outfp : $result;
1259
	}
1260
	
1261
	/**
1262
	 * Get remote contents with fsockopen()
1263
	 *
1264
	 * @param  string   $url          url
1265
	 * @param  int      $timeout      timeout (sec)
1266
	 * @param  int      $redirect_max redirect max count
1267
	 * @param  string   $ua
1268
	 * @param  resource $outfp
1269
	 * @return string or bool(false)
1270
	 * @retval string contents
1271
	 * @retval false  error
1272
	 * @author Naoki Sawada
1273
	 */
1274
	protected function fsock_get_contents( &$url, $timeout, $redirect_max, $ua, $outfp ) {
1275
1276
		$connect_timeout = 3;
1277
		$connect_try = 3;
1278
		$method = 'GET';
1279
		$readsize = 4096;
1280
1281
		$getSize = null;
1282
		$headers = '';
1283
		
1284
		$arr = parse_url($url);
1285
		if (!$arr){
1286
			// Bad request
1287
			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...
1288
		}
1289
		
1290
		// query
1291
		$arr['query'] = isset($arr['query']) ? '?'.$arr['query'] : '';
1292
		// port
1293
		$arr['port'] = isset($arr['port']) ? $arr['port'] : (!empty($arr['https'])? 443 : 80);
1294
		
1295
		$url_base = $arr['scheme'].'://'.$arr['host'].':'.$arr['port'];
1296
		$url_path = isset($arr['path']) ? $arr['path'] : '/';
1297
		$uri = $url_path.$arr['query'];
1298
		
1299
		$query = $method.' '.$uri." HTTP/1.0\r\n";
1300
		$query .= "Host: ".$arr['host']."\r\n";
1301
		if (!empty($ua)) $query .= "User-Agent: ".$ua."\r\n";
1302
		if (!is_null($getSize)) $query .= 'Range: bytes=0-' . ($getSize - 1) . "\r\n";
1303
		
1304
		$query .= $headers;
1305
1306
		$query .= "\r\n";
1307
1308
		$fp = $connect_try_count = 0;
1309
		while( !$fp && $connect_try_count < $connect_try ) {
1310
	
1311
			$errno = 0;
1312
			$errstr = "";
1313
			$fp = @ fsockopen(
1314
			$arr['https'].$arr['host'],
1315
			$arr['port'],
1316
			$errno,$errstr,$connect_timeout);
1317
			if ($fp) break;
1318
			$connect_try_count++;
1319
			if (connection_aborted()) {
1320
				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...
1321
			}
1322
			sleep(1); // wait 1sec
1323
		}
1324
		
1325
		$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...
1326
		for ($written = 0; $written < strlen($query); $written += $fwrite) {
1327
			$fwrite = fwrite($fp, substr($query, $written));
1328
			if (!$fwrite) {
1329
				break;
1330
			}
1331
		}
1332
		
1333
		$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...
1334
		
1335
		if ($timeout) {
1336
			socket_set_timeout($fp, $timeout);
1337
		}
1338
		
1339
		$_response = '';
1340
		$header = '';
1341
		while($_response !== "\r\n"){
1342
			$_response = fgets($fp, $readsize);
1343
			$header .= $_response;
1344
		};
1345
		
1346
		$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...
1347
		$rc = (int)$rccd[1];
1348
		
1349
		// Redirect
1350
		switch ($rc) {
1351
			case 307: // Temporary Redirect
1352
			case 303: // See Other
1353
			case 302: // Moved Temporarily
1354
			case 301: // Moved Permanently
1355
				$matches = array();
1356
				if (preg_match('/^Location: (.+?)(#.+)?$/im',$header,$matches) && --$redirect_max > 0) {
1357
					$url = trim($matches[1]);
1358
					$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...
1359
					if (!preg_match('/^https?:\//',$url)) { // no scheme
1360
						if ($url{0} != '/') { // Relative path
1361
							// to Absolute path
1362
							$url = substr($url_path,0,strrpos($url_path,'/')).'/'.$url;
1363
						}
1364
						// add sheme,host
1365
						$url = $url_base.$url;
1366
					}
1367
					fclose($fp);
1368
					return $this->fsock_get_contents( $url, $timeout, $redirect_max, $ua, $outfp );
1369
				}
1370
		}
1371
		
1372
		$body = '';
1373
		if (!$outfp) {
1374
			$outfp = fopen('php://temp', 'rwb');
1375
			$body = true;
1376
		}
1377
		while(fwrite($outfp, fread($fp, $readsize))) {
1378
			if ($timeout) {
1379
				$_status = socket_get_status($fp);
1380
				if ($_status['timed_out']) {
1381
					fclose($outfp);
1382
					fclose($fp);
1383
					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...
1384
				}
1385
			}
1386
		}
1387
		if ($body) {
1388
			rewind($outfp);
1389
			$body = stream_get_contents($outfp);
1390
			fclose($outfp);
1391
			$outfp = null;
1392
		}
1393
		
1394
		fclose($fp);
1395
		
1396
		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 1396 which is incompatible with the return type documented by elFinder::fsock_get_contents of type string.
Loading history...
1397
	}
1398
	
1399
	/**
1400
	 * Parse Data URI scheme
1401
	 * 
1402
	 * @param  string $str
1403
	 * @param  array  $extTable
1404
	 * @return array
1405
	 * @author Naoki Sawada
1406
	 */
1407
	protected function parse_data_scheme( $str, $extTable ) {
1408
		$data = $name = '';
1409
		if ($fp = fopen('data://'.substr($str, 5), 'rb')) {
1410 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...
1411
				$meta = stream_get_meta_data($fp);
1412
				$ext = isset($extTable[$meta['mediatype']])? '.' . $extTable[$meta['mediatype']] : '';
1413
				$name = substr(md5($data), 0, 8) . $ext;
1414
			}
1415
			fclose($fp);
1416
		}
1417
		return array($data, $name);
1418
	}
1419
	
1420
	/**
1421
	 * Detect file type extension by local path
1422
	 * 
1423
	 * @param  string $path Local path
1424
	 * @return string file type extension with dot
1425
	 * @author Naoki Sawada
1426
	 */
1427
	protected function detectFileExtension($path) {
1428
		static $type, $finfo, $extTable;
1429
		if (!$type) {
1430
			$keys = array_keys($this->volumes);
1431
			$volume = $this->volumes[$keys[0]];
1432
			$extTable = array_flip(array_unique($volume->getMimeTable()));
1433
			
1434
			if (class_exists('finfo', false)) {
1435
				$tmpFileInfo = @explode(';', @finfo_file(finfo_open(FILEINFO_MIME), __FILE__));
1436
			} else {
1437
				$tmpFileInfo = false;
1438
			}
1439
			$regexp = '/text\/x\-(php|c\+\+)/';
1440
			if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) {
1441
				$type = 'finfo';
1442
				$finfo = finfo_open(FILEINFO_MIME);
1443
			} elseif (function_exists('mime_content_type')
1444
					&& 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...
1445
				$type = 'mime_content_type';
1446
			} elseif (function_exists('getimagesize')) {
1447
				$type = 'getimagesize';
1448
			} else {
1449
				$type = 'none';
1450
			}
1451
		}
1452
		
1453
		$mime = '';
1454
		if ($type === 'finfo') {
1455
			$mime = @finfo_file($finfo, $path);
1456
		} elseif ($type === 'mime_content_type') {
1457
			$mime = mime_content_type($path);
1458
		} elseif ($type === 'getimagesize') {
1459
			if ($img = @getimagesize($path)) {
1460
				$mime = $img['mime'];
1461
			}
1462
		}
1463
		
1464
		if ($mime) {
1465
			$mime = explode(';', $mime);
1466
			$mime = trim($mime[0]);
1467
			
1468 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...
1469
				// finfo return this mime for empty files
1470
				$mime = 'text/plain';
1471
			} elseif ($mime == 'application/x-zip') {
1472
				// http://elrte.org/redmine/issues/163
1473
				$mime = 'application/zip';
1474
			}
1475
		}
1476
		
1477
		return ($mime && isset($extTable[$mime]))? ('.' . $extTable[$mime]) : '';
1478
	}
1479
	
1480
	/**
1481
	 * Get temporary dirctroy path
1482
	 * 
1483
	 * @param  string $volumeTempPath
1484
	 * @return string
1485
	 * @author Naoki Sawada
1486
	 */
1487
	private function getTempDir($volumeTempPath = null) {
1488
		$testDirs = array();
1489
		if ($this->uploadTempPath) {
1490
			$testDirs[] = rtrim(realpath($this->uploadTempPath), DIRECTORY_SEPARATOR);
1491
		}
1492
		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...
1493
			$testDirs[] = rtrim(realpath($volumeTempPath), DIRECTORY_SEPARATOR);
1494
		}
1495
		if (function_exists('sys_get_temp_dir')) {
1496
			$testDirs[] = sys_get_temp_dir();
1497
		}
1498
		$tempDir = '';
1499
		foreach($testDirs as $testDir) {
1500
			if (!$testDir || !is_dir($testDir)) continue;
1501
			if (is_writable($testDir)) {
1502
				$tempDir = $testDir;
1503
				$gc = time() - 3600;
1504
				foreach(glob($tempDir . DIRECTORY_SEPARATOR .'ELF*') as $cf) {
1505
					if (filemtime($cf) < $gc) {
1506
						@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...
1507
					}
1508
				}
1509
				break;
1510
			}
1511
		}
1512
		return $tempDir;
1513
	}
1514
	
1515
	/**
1516
	 * chmod
1517
	 *
1518
	 * @param array  command arguments
1519
	 * @return array
1520
	 * @author David Bartle
1521
	 **/
1522
	protected function chmod($args) {
1523
		$targets = $args['targets'];
1524
		$mode    = intval((string)$args['mode'], 8);
1525
1526
		if (!is_array($targets)) {
1527
			$targets = array($targets);
1528
		}
1529
		
1530
		$result = array();
1531
		
1532 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...
1533
			$result['error'] = $this->error(self::ERROR_CONF_NO_VOL);
1534
			return $result;
1535
		}
1536
1537
		$files = array();
1538
		$errors = array();
1539
		foreach($targets as $target) {
1540
			$file = $volume->chmod($target, $mode);
1541
			if ($file) {
1542
				$files = array_merge($files, is_array($file)? $file : array($file));
1543
			} else {
1544
				$errors = array_merge($errors, $volume->error());
1545
			}
1546
		}
1547
		
1548
		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...
1549
			$result['changed'] = $files;
1550
			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...
1551
				$result['warning'] = $this->error($errors);
1552
			}
1553
		} else {
1554
			$result['error'] = $this->error($errors);
1555
		}
1556
		
1557
		return $result;
1558
	}
1559
	
1560
	/**
1561
	 * Check chunked upload files
1562
	 * 
1563
	 * @param string $tmpname  uploaded temporary file path
1564
	 * @param string $chunk    uploaded chunk file name
1565
	 * @param string $cid      uploaded chunked file id
1566
	 * @param string $tempDir  temporary dirctroy path
1567
	 * @return array (string JoinedTemporaryFilePath, string FileName) or (empty, empty)
1568
	 * @author Naoki Sawada
1569
	 */
1570
	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...
1571
		if (preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m)) {
1572
			$fname = $m[1];
1573
			$encname = md5($cid . '_' . $fname);
1574
			$base = $tempDir . DIRECTORY_SEPARATOR . 'ELF' . $encname;
1575
			$clast = intval($m[3]);
1576
			if (is_null($tmpname)) {
1577
				ignore_user_abort(true);
1578
				sleep(10); // wait 10 sec
1579
				// chunked file upload fail
1580
				foreach(glob($base . '*') as $cf) {
1581
					@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...
1582
				}
1583
				ignore_user_abort(false);
1584
				return;
1585
			}
1586
			
1587
			$range = isset($_POST['range'])? trim($_POST['range']) : '';
1588
			if ($range && preg_match('/^(\d+),(\d+),(\d+)$/', $range, $ranges)) {
1589
				$start = $ranges[1];
1590
				$len   = $ranges[2];
1591
				$size  = $ranges[3];
1592
				$tmp = $base . '.part';
1593
				$csize = filesize($tmpname);
1594
				
1595
				$tmpExists = is_file($tmp);
1596
				if (!$tmpExists) {
1597
					// check upload max size
1598
					$uploadMaxSize = $volume->getUploadMaxSize();
1599
					if ($uploadMaxSize > 0 && $size > $uploadMaxSize) {
1600
						return array(self::ERROR_UPLOAD_FILE_SIZE, false);
1601
					}
1602
					// make temp file
1603
					$ok = false;
1604
					if ($fp = fopen($tmp, 'wb')) {
1605
						flock($fp, LOCK_EX);
1606
						$ok = ftruncate($fp, $size);
1607
						flock($fp, LOCK_UN);
1608
						fclose($fp);
1609
						touch($base);
1610
					}
1611
					if (!$ok) {
1612
						unlink($tmp);
1613
						return array(self::ERROR_UPLOAD_TEMP, false);
1614
					}
1615
				} else {
1616
					// wait until makeing temp file (for anothor session)
1617
					$cnt = 1200; // Time limit 120 sec
1618
					while(!is_file($base) && --$cnt) {
1619
						usleep(100000); // wait 100ms
1620
					}
1621
					if (!$cnt) {
1622
						return array(self::ERROR_UPLOAD_TEMP, false);
1623
					}
1624
				}
1625
				
1626
				// check size info
1627
				if ($len != $csize || $start + $len > $size || ($tmpExists && $size != filesize($tmp))) {
1628
					return array(self::ERROR_UPLOAD_TEMP, false);
1629
				}
1630
				
1631
				// write chunk data
1632
				$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...
1633
				$src = fopen($tmpname, 'rb');
1634
				$fp = fopen($tmp, 'cb');
1635
				fseek($fp, $start);
1636
				$writelen = stream_copy_to_stream($src, $fp, $len);
1637
				fclose($fp);
1638
				fclose($src);
1639
				if ($writelen != $len) {
1640
					return array(self::ERROR_UPLOAD_TEMP, false);
1641
				}
1642
				
1643
				// write counts
1644
				file_put_contents($base, "\0", FILE_APPEND | LOCK_EX);
1645
				
1646
				if (filesize($base) >= $clast + 1) {
1647
					// Completion
1648
					unlink($base);
1649
					return array($tmp, $fname);
1650
				}
1651
			} else {
1652
				// old way
1653
				$part = $base . $m[2];
1654
				if (move_uploaded_file($tmpname, $part)) {
1655
					@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...
1656
					if ($clast < count(glob($base . '*'))) {
1657
						$parts = array();
1658
						for ($i = 0; $i <= $clast; $i++) {
1659
							$name = $base . '.' . $i . '_' . $clast;
1660
							if (is_readable($name)) {
1661
								$parts[] = $name;
1662
							} else {
1663
								$parts = null;
1664
								break;
1665
							}
1666
						}
1667
						if ($parts) {
1668
							if (!is_file($base)) {
1669
								touch($base);
1670
								if ($resfile = tempnam($tempDir, 'ELF')) {
1671
									$target = fopen($resfile, 'wb');
1672
									foreach($parts as $f) {
1673
										$fp = fopen($f, 'rb');
1674
										while (!feof($fp)) {
1675
											fwrite($target, fread($fp, 8192));
1676
										}
1677
										fclose($fp);
1678
										unlink($f);
1679
									}
1680
									fclose($target);
1681
									unlink($base);
1682
									return array($resfile, $fname);
1683
								}
1684
								unlink($base);
1685
							}
1686
						}
1687
					}
1688
				}
1689
			}
1690
		}
1691
		return array('', '');
1692
	}
1693
	
1694
	/**
1695
	 * Save uploaded files
1696
	 *
1697
	 * @param  array
1698
	 * @return array
1699
	 * @author Dmitry (dio) Levashov
1700
	 **/
1701
	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...
1702
		$ngReg  = '/[\/\\?*:|"<>]/';
1703
		$target = $args['target'];
1704
		$volume = $this->volume($target);
1705
		$files  = isset($args['FILES']['upload']) && is_array($args['FILES']['upload']) ? $args['FILES']['upload'] : array();
1706
		$header = empty($args['html']) ? array() : array('header' => 'Content-Type: text/html; charset=utf-8');
1707
		$result = array_merge(array('added' => array()), $header);
1708
		$paths  = $args['upload_path']? $args['upload_path'] : array();
1709
		$chunk  = $args['chunk']? $args['chunk'] : '';
1710
		$cid    = $args['cid']? (int)$args['cid'] : '';
1711
		
1712
		$renames= array();
1713
		$suffix = '~';
1714
		if ($args['renames'] && is_array($args['renames'])) {
1715
			$renames = array_flip($args['renames']);
1716 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...
1717
				$suffix = $args['suffix'];
1718
			}
1719
		}
1720
		
1721
		if (!$volume) {
1722
			return array_merge(array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target)), $header);
1723
		}
1724
		
1725
		// regist Shutdown function
1726
		$GLOBALS['elFinderTempFiles'] = array();
1727
// 		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...
1728
// 			$shutdownfunc = function(){ // <- Parse error on PHP < 5.3 ;-(
1729
// 				foreach(array_keys($GLOBALS['elFinderTempFiles']) as $f){
1730
// 					@unlink($f);
1731
// 				}
1732
// 			};
1733
// 		} else {
1734
			$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...
1735
				foreach(array_keys($GLOBALS[\'elFinderTempFiles\']) as $f){
1736
					@unlink($f);
1737
				}
1738
			');
1739
//		}
1740
		register_shutdown_function($shutdownfunc);
1741
		
1742
		// file extentions table by MIME
1743
		$extTable = array_flip(array_unique($volume->getMimeTable()));
1744
		
1745
		if (empty($files)) {
1746
			if (!$args['upload'] && $args['name'] && is_array($args['name'])) {
1747
				$error = '';
1748
				$result['name'] = array();
1749
				foreach($args['name'] as $_i => $_name) {
1750
					if (!$volume->isUploadableByName($_name)) {
1751
						$error = $this->error(self::ERROR_UPLOAD_FILE, $_name, self::ERROR_UPLOAD_FILE_MIME);
1752
						break;
1753
					}
1754
					$result['name'][$_i] = preg_replace($ngReg, '_', $_name);
1755
				}
1756
				if ($error) {
1757
					$result['error'] = $error;
1758
					return $result;
1759
				}
1760
				$result = array_merge_recursive($result, $this->ls($args));
1761
				if (empty($result['list'])) {
1762
					$result['name'] = array();
1763
				} else {
1764
					$result['name'] = array_merge(array_intersect($result['name'], $result['list']));
1765
				}
1766
				return $result;
1767
			}
1768
			if (isset($args['upload']) && is_array($args['upload']) && ($tempDir = $this->getTempDir($volume->getTempPath()))) {
1769
				$names = array();
1770
				foreach($args['upload'] as $i => $url) {
1771
					// check chunked file upload commit
1772
					if ($args['chunk']) {
1773
						if ($url === 'chunkfail' && $args['mimes'] === 'chunkfail') {
1774
							$this->checkChunkedFile(null, $chunk, $cid, $tempDir);
1775
							if (preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m)) {
1776
								$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $m[1], self::ERROR_UPLOAD_TRANSFER);
1777
							}
1778
							return $result;
1779
						} else {
1780
							$tmpfname = $tempDir . '/' . $args['chunk'];
1781
							$files['tmp_name'][$i] = $tmpfname;
1782
							$files['name'][$i] = $url;
1783
							$files['error'][$i] = 0;
1784
							$GLOBALS['elFinderTempFiles'][$tmpfname] = true;
1785
							break;
1786
						}
1787
					}
1788
					
1789
					$tmpfname = $tempDir . DIRECTORY_SEPARATOR . 'ELF_FATCH_' . md5($url.microtime(true));
1790
					
1791
					$_name = '';
1792
					// check is data:
1793
					if (substr($url, 0, 5) === 'data:') {
1794
						list($data, $args['name'][$i]) = $this->parse_data_scheme($url, $extTable);
1795
					} else {
1796
						$fp = fopen($tmpfname, 'wb');
1797
						$data = $this->get_remote_contents($url, 30, 5, 'Mozilla/5.0', $fp);
1798
						$_POST['overwrite'] = false;
1799
						$_name = preg_replace('~^.*?([^/#?]+)(?:\?.*)?(?:#.*)?$~', '$1', rawurldecode($url));
1800
						// Check `Content-Disposition` response header
1801
						if ($data && ($headers = get_headers($url, true)) && !empty($headers['Content-Disposition'])) {
1802
							if (preg_match('/filename\*?=(?:(.+?)\'\')?"?([a-z0-9_.~%-]+)"?/i', $headers['Content-Disposition'], $m)) {
1803
								$_name = rawurldecode($m[2]);
1804
								if ($m[1] && strtoupper($m[1]) !== 'UTF-8' && function_exists('mb_convert_encoding')) {
1805
									$_name = mb_convert_encoding($_name, 'UTF-8', $m[1]);
1806
								}
1807
							}
1808
						}
1809
					}
1810
					if ($data) {
1811
						if (isset($args['name'][$i])) {
1812
							$_name = $args['name'][$i];
1813
						}
1814
						if ($_name) {
1815
							$_ext = '';
1816
							if (preg_match('/(\.[a-z0-9]{1,7})$/', $_name, $_match)) {
1817
								$_ext = $_match[1];
1818
							}
1819
							if ((is_resource($data) && fclose($data)) || file_put_contents($tmpfname, $data)) {
1820
								$GLOBALS['elFinderTempFiles'][$tmpfname] = true;
1821
								$_name = preg_replace($ngReg, '_', $_name);
1822
								list($_a, $_b) = array_pad(explode('.', $_name, 2), 2, '');
1823
								if ($_b === '') {
1824
									if ($_ext) {
1825
										rename($tmpfname, $tmpfname . $_ext);
1826
										$tmpfname = $tmpfname . $_ext;
1827
									}
1828
									$_b = $this->detectFileExtension($tmpfname);
1829
									$_name = $_a.$_b;
1830
								} else {
1831
									$_b = '.'.$_b;
1832
								}
1833
								if (isset($names[$_name])) {
1834
									$_name = $_a.'_'.$names[$_name]++.$_b;
1835
								} else {
1836
									$names[$_name] = 1;
1837
								}
1838
								$files['tmp_name'][$i] = $tmpfname;
1839
								$files['name'][$i] = $_name;
1840
								$files['error'][$i] = 0;
1841
							} else {
1842
								@ 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...
1843
							}
1844
						}
1845
					}
1846
				}
1847
			}
1848
			if (empty($files)) {
1849
				return array_merge(array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_UPLOAD_NO_FILES)), $header);
1850
			}
1851
		}
1852
		
1853
		foreach ($files['name'] as $i => $name) {
1854
			if (($error = $files['error'][$i]) > 0) {
1855
				$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);
1856
				$this->uploadDebug = 'Upload error code: '.$error;
1857
				break;
1858
			}
1859
			
1860
			$tmpname = $files['tmp_name'][$i];
1861
			$path = ($paths && !empty($paths[$i]))? $paths[$i] : '';
1862
			if ($name === 'blob') {
1863
				if ($chunk) {
1864
					if ($tempDir = $this->getTempDir($volume->getTempPath())) {
1865
						list($tmpname, $name) = $this->checkChunkedFile($tmpname, $chunk, $cid, $tempDir, $volume);
1866
						if ($tmpname) {
1867
							if ($name === false) {
1868
								preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m);
1869
								$result['error'] = $this->error(self::ERROR_UPLOAD_FILE, $m[1], $tmpname);
1870
								$result['_chunkfailure'] = true;
1871
								$this->uploadDebug = 'Upload error: ' . $tmpname;
1872
							} else if ($name) {
1873
								$result['_chunkmerged'] = basename($tmpname);
1874
								$result['_name'] = $name;
1875
							}
1876
						}
1877
					} else {
1878
						$result['error'] = $this->error(self::ERROR_UPLOAD_FILE, $chunk, self::ERROR_UPLOAD_TRANSFER);
1879
						$this->uploadDebug = 'Upload error: unable open tmp file';
1880
					}
1881
					return $result;
1882 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...
1883
					// for form clipboard with Google Chrome
1884
					$type = $files['type'][$i];
1885
					$ext = isset($extTable[$type])? '.' . $extTable[$type] : '';
1886
					$name = substr(md5(basename($tmpname)), 0, 8) . $ext;
1887
				}
1888
			}
1889
			
1890
			// 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...
1891
			if (! empty($this->listeners['upload.presave'])) {
1892 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...
1893
					call_user_func_array($handler, array(&$path, &$name, $tmpname, $this, $volume));
1894
				}
1895
			}
1896
			
1897
			if (($fp = fopen($tmpname, 'rb')) == false) {
1898
				$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, self::ERROR_UPLOAD_TRANSFER);
1899
				$this->uploadDebug = 'Upload error: unable open tmp file';
1900
				if (! is_uploaded_file($tmpname)) {
1901
					if (@ unlink($tmpname)) unset($GLOBALS['elFinderTempFiles'][$tmpfname]);
1902
					continue;
1903
				}
1904
				break;
1905
			}
1906
			$rnres = array();
1907
			if ($path) {
1908
				$_target = $volume->getUploadTaget($target, $path, $result);
1909
			} else {
1910
				$_target = $target;
1911
				// file rename for backup
1912
				if (isset($renames[$name])) {
1913
					$dir = $volume->realpath($_target);
1914
					$hash = $volume->getHash($dir, $name);
1915
					$rnres = $this->rename(array('target' => $hash, 'name' => $volume->uniqueName($dir, $name, $suffix, true, 0)));
1916
					if (!empty($rnres['error'])) {
1917
						$result['warning'] = $rnres['error'];
1918
						break;
1919
					}
1920
				}
1921
			}
1922
			if (! $_target || ($file = $volume->upload($fp, $_target, $name, $tmpname)) === false) {
1923
				$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $volume->error());
1924
				fclose($fp);
1925 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...
1926
					if (@ unlink($tmpname)) unset($GLOBALS['elFinderTempFiles'][$tmpname]);;
1927
					continue;
1928
				}
1929
				break;
1930
			}
1931
			
1932
			is_resource($fp) && fclose($fp);
1933
			if (! is_uploaded_file($tmpname)){
1934
				clearstatcache();
1935 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...
1936
					unset($GLOBALS['elFinderTempFiles'][$tmpname]);
1937
				}
1938
			}
1939
			$result['added'][] = $file;
1940
			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...
1941
				$result = array_merge_recursive($result, $rnres);
1942
			}
1943
		}
1944
		if ($GLOBALS['elFinderTempFiles']) {
1945
			foreach(array_keys($GLOBALS['elFinderTempFiles']) as $_temp) {
1946
				@ 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...
1947
			}
1948
		}
1949
		$result['removed'] = $volume->removed();
1950
		
1951
		if (!empty($args['node'])) {
1952
			$result['callback'] = array(
1953
				'node' => $args['node'],
1954
				'bind' => 'upload'
1955
			);
1956
		}
1957
		return $result;
1958
	}
1959
		
1960
	/**
1961
	 * Copy/move files into new destination
1962
	 *
1963
	 * @param  array  command arguments
1964
	 * @return array
1965
	 * @author Dmitry (dio) Levashov
1966
	 **/
1967
	protected function paste($args) {
1968
		$dst     = $args['dst'];
1969
		$targets = is_array($args['targets']) ? $args['targets'] : array();
1970
		$cut     = !empty($args['cut']);
1971
		$error   = $cut ? self::ERROR_MOVE : self::ERROR_COPY;
1972
		$result  = array('added' => array(), 'removed' => array());
1973
		
1974
		if (($dstVolume = $this->volume($dst)) == false) {
1975
			return array('error' => $this->error($error, '#'.$targets[0], self::ERROR_TRGDIR_NOT_FOUND, '#'.$dst));
1976
		}
1977
		
1978
		$renames = array();
1979
		$suffix = '~';
1980
		if (!empty($args['renames'])) {
1981
			$renames = array_flip($args['renames']);
1982 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...
1983
				$suffix = $args['suffix'];
1984
			}
1985
		}
1986
		
1987
		foreach ($targets as $target) {
1988 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...
1989
				$result['warning'] = $this->error($error, '#'.$target, self::ERROR_FILE_NOT_FOUND);
1990
				break;
1991
			}
1992
			
1993
			$rnres = array();
1994
			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...
1995
				$file = $srcVolume->file($target);
1996
				if (isset($renames[$file['name']])) {
1997
					$dir = $dstVolume->realpath($dst);
1998
					$hash = $dstVolume->getHash($dir, $file['name']);
1999
					$rnres = $this->rename(array('target' => $hash, 'name' => $dstVolume->uniqueName($dir, $file['name'], $suffix, true, 0)));
2000
					if (!empty($rnres['error'])) {
2001
						$result['warning'] = $rnres['error'];
2002
						break;
2003
					}
2004
				}
2005
			}
2006
			
2007
			if (($file = $dstVolume->paste($srcVolume, $target, $dst, $cut)) == false) {
2008
				$result['warning'] = $this->error($dstVolume->error());
2009
				break;
2010
			}
2011
			
2012
			$result['added'][] = $file;
2013
			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...
2014
				$result = array_merge_recursive($result, $rnres);
2015
			}
2016
		}
2017
		return $result;
2018
	}
2019
	
2020
	/**
2021
	 * Return file content
2022
	 *
2023
	 * @param  array  $args  command arguments
2024
	 * @return array
2025
	 * @author Dmitry (dio) Levashov
2026
	 **/
2027
	protected function get($args) {
2028
		$target = $args['target'];
2029
		$volume = $this->volume($target);
2030
		
2031
		if (!$volume || ($file = $volume->file($target)) == false) {
2032
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target, self::ERROR_FILE_NOT_FOUND));
2033
		}
2034
		
2035 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...
2036
			return array('error' => $this->error(self::ERROR_OPEN, $volume->path($target), $volume->error()));
2037
		}
2038
		
2039
		if ($args['conv'] && function_exists('mb_detect_encoding') && function_exists('mb_convert_encoding')) {
2040
			$mime = isset($file['mime'])? $file['mime'] : '';
2041
			if ($mime && strtolower(substr($mime, 0, 4)) === 'text') {
2042
				if ($enc = mb_detect_encoding ( $content , mb_detect_order(), true)) {
2043
					if (strtolower($enc) !== 'utf-8') {
2044
						$content = mb_convert_encoding($content, 'UTF-8', $enc);
2045
					}
2046
				}
2047
			}
2048
		}
2049
		
2050
		$json = json_encode($content);
2051
2052
		if ($json === false || strlen($json) < strlen($content)) {
2053
			if ($args['conv']) {
2054
				return array('error' => $this->error(self::ERROR_CONV_UTF8,self::ERROR_NOT_UTF8_CONTENT, $volume->path($target)));
2055
			} else {
2056
				return array('doconv' => true);
2057
			}
2058
		}
2059
		
2060
		return array('content' => $content);
2061
	}
2062
	
2063
	/**
2064
	 * Save content into text file
2065
	 *
2066
	 * @return array
2067
	 * @author Dmitry (dio) Levashov
2068
	 **/
2069
	protected function put($args) {
2070
		$target = $args['target'];
2071
		
2072 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...
2073
		|| ($file = $volume->file($target)) == false) {
2074
			return array('error' => $this->error(self::ERROR_SAVE, '#'.$target, self::ERROR_FILE_NOT_FOUND));
2075
		}
2076
		
2077 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...
2078
			return array('error' => $this->error(self::ERROR_SAVE, $volume->path($target), $volume->error()));
2079
		}
2080
		
2081
		return array('changed' => array($file));
2082
	}
2083
2084
	/**
2085
	 * Extract files from archive
2086
	 *
2087
	 * @param  array  $args  command arguments
2088
	 * @return array
2089
	 * @author Dmitry (dio) Levashov, 
2090
	 * @author Alexey Sukhotin
2091
	 **/
2092
	protected function extract($args) {
2093
		$target = $args['target'];
2094
		$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...
2095
		$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...
2096
		$makedir = isset($args['makedir'])? (bool)$args['makedir'] : null;
2097
2098 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...
2099
		|| ($file = $volume->file($target)) == false) {
2100
			return array('error' => $this->error(self::ERROR_EXTRACT, '#'.$target, self::ERROR_FILE_NOT_FOUND));
2101
		}  
2102
2103
		return ($file = $volume->extract($target, $makedir))
2104
			? array('added' => isset($file['read'])? array($file) : $file)
2105
			: array('error' => $this->error(self::ERROR_EXTRACT, $volume->path($target), $volume->error()));
2106
	}
2107
	
2108
	/**
2109
	 * Create archive
2110
	 *
2111
	 * @param  array  $args  command arguments
2112
	 * @return array
2113
	 * @author Dmitry (dio) Levashov, 
2114
	 * @author Alexey Sukhotin
2115
	 **/
2116
	protected function archive($args) {
2117
		$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...
2118
		$targets = isset($args['targets']) && is_array($args['targets']) ? $args['targets'] : array();
2119
		$name    = isset($args['name'])? $args['name'] : '';
2120
	
2121 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...
2122
			return $this->error(self::ERROR_ARCHIVE, self::ERROR_TRGDIR_NOT_FOUND);
2123
		}
2124
	
2125
		return ($file = $volume->archive($targets, $args['type'], $name))
2126
			? array('added' => array($file))
2127
			: array('error' => $this->error(self::ERROR_ARCHIVE, $volume->error()));
2128
	}
2129
	
2130
	/**
2131
	 * Search files
2132
	 *
2133
	 * @param  array  $args  command arguments
2134
	 * @return array
2135
	 * @author Dmitry Levashov
2136
	 **/
2137
	protected function search($args) {
2138
		$q      = trim($args['q']);
2139
		$mimes  = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array();
2140
		$target = !empty($args['target'])? $args['target'] : null;
2141
		$result = array();
2142
2143
		if (!is_null($target)) {
2144
			$volume = $this->volume($target);
2145
			$result = $volume->search($q, $mimes, $target);
2146
		} else {
2147
			foreach ($this->volumes as $volume) {
2148
				$result = array_merge($result, $volume->search($q, $mimes));
2149
			}
2150
		}
2151
		
2152
		return array('files' => $result);
2153
	}
2154
	
2155
	/**
2156
	 * Return file info (used by client "places" ui)
2157
	 *
2158
	 * @param  array  $args  command arguments
2159
	 * @return array
2160
	 * @author Dmitry Levashov
2161
	 **/
2162
	protected function info($args) {
2163
		$files = array();
2164
		$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...
2165
		$compare = null;
2166
		// long polling mode
2167
		if ($args['compare'] && count($args['targets']) === 1) {
2168
			$compare = intval($args['compare']);
2169
			$hash = $args['targets'][0];
2170
			if ($volume = $this->volume($hash)) {
2171
				$standby = (int)$volume->getOption('plStandby');
2172
				$_compare = false;
2173
				if (($syncCheckFunc = $volume->getOption('syncCheckFunc')) && is_callable($syncCheckFunc)) {
2174
					$_compare = call_user_func_array($syncCheckFunc, array($volume->realpath($hash), $standby, $compare, $volume, $this));
2175
				}
2176
				if ($_compare !== false) {
2177
					$compare = $_compare;
2178
				} else {
2179
					$sleep = max(1, (int)$volume->getOption('tsPlSleep'));
2180
					$limit = max(1, $standby / $sleep) + 1;
2181
					$timelimit = ini_get('max_execution_time');
2182
					do {
2183
						$timelimit && @ set_time_limit($timelimit + $sleep);
2184
						$volume->clearstatcache();
2185
						if (($info = $volume->file($hash)) != false) {
2186
							if ($info['ts'] != $compare) {
2187
								$compare = $info['ts'];
2188
								break;
2189
							}
2190
						} else {
2191
							$compare = 0;
2192
							break;
2193
						}
2194
						if (--$limit) {
2195
							sleep($sleep);
2196
						}
2197
					} while($limit);
2198
				}
2199
			}
2200
		} else {
2201
			foreach ($args['targets'] as $hash) {
2202
				if (($volume = $this->volume($hash)) != false
2203
				&& ($info = $volume->file($hash)) != false) {
2204
					$files[] = $info;
2205
				}
2206
			}
2207
		}
2208
		
2209
		$result = array('files' => $files);
2210
		if (!is_null($compare)) {
2211
			$result['compare'] = strval($compare);
2212
		}
2213
		return $result;
2214
	}
2215
	
2216
	/**
2217
	 * Return image dimmensions
2218
	 *
2219
	 * @param  array  $args  command arguments
2220
	 * @return array
2221
	 * @author Dmitry (dio) Levashov
2222
	 **/
2223
	protected function dim($args) {
2224
		$target = $args['target'];
2225
		
2226
		if (($volume = $this->volume($target)) != false) {
2227
			$dim = $volume->dimensions($target);
2228
			return $dim ? array('dim' => $dim) : array();
2229
		}
2230
		return array();
2231
	}
2232
	
2233
	/**
2234
	 * Resize image
2235
	 *
2236
	 * @param  array  command arguments
2237
	 * @return array
2238
	 * @author Dmitry (dio) Levashov
2239
	 * @author Alexey Sukhotin
2240
	 **/
2241
	protected function resize($args) {
2242
		$target = $args['target'];
2243
		$width  = $args['width'];
2244
		$height = $args['height'];
2245
		$x      = (int)$args['x'];
2246
		$y      = (int)$args['y'];
2247
		$mode   = $args['mode'];
2248
		$bg     = null;
2249
		$degree = (int)$args['degree'];
2250
		$quality= (int)$args['quality'];
2251
		
2252 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...
2253
		|| ($file = $volume->file($target)) == false) {
2254
			return array('error' => $this->error(self::ERROR_RESIZE, '#'.$target, self::ERROR_FILE_NOT_FOUND));
2255
		}
2256
2257
		return ($file = $volume->resize($target, $width, $height, $x, $y, $mode, $bg, $degree, $quality))
2258
			? array('changed' => array($file))
2259
			: array('error' => $this->error(self::ERROR_RESIZE, $volume->path($target), $volume->error()));
2260
	}
2261
	
2262
	/**
2263
	* Return content URL
2264
	*
2265
	* @param  array  $args  command arguments
2266
	* @return array
2267
	* @author Naoki Sawada
2268
	**/
2269
	protected function url($args) {
2270
		$target = $args['target'];
2271
		$options = isset($args['options'])? $args['options'] : array();
2272
		if (($volume = $this->volume($target)) != false) {
2273
			$url = $volume->getContentUrl($target, $options);
2274
			return $url ? array('url' => $url) : array();
2275
		}
2276
		return array();
2277
	}
2278
2279
	/**
2280
	 * Output callback result with JavaScript that control elFinder
2281
	 * or HTTP redirect to callbackWindowURL
2282
	 * 
2283
	 * @param  array  command arguments
2284
	 * @author Naoki Sawada
2285
	 */
2286
	protected function callback($args) {
2287
		$checkReg = '/[^a-zA-Z0-9;._-]/';
2288
		$node = (isset($args['node']) && !preg_match($checkReg, $args['node']))? $args['node'] : '';
2289
		$json = (isset($args['json']) && @json_decode($args['json']))? $args['json'] : '{}';
2290
		$bind  = (isset($args['bind']) && !preg_match($checkReg, $args['bind']))? $args['bind'] : '';
2291
		$done = (!empty($args['done']));
2292
		
2293
		while( ob_get_level() ) {
2294
			if (! ob_end_clean()) {
2295
				break;
2296
			}
2297
		}
2298
		
2299
		if ($done || ! $this->callbackWindowURL) {
2300
			$script = '';
2301
			if ($node) {
2302
				$script .= '
2303
					var w = window.opener || window.parent || window;
2304
					try {
2305
						var elf = w.document.getElementById(\''.$node.'\').elfinder;
2306
						if (elf) {
2307
							var data = '.$json.';
2308
							if (data.error) {
2309
								elf.error(data.error);
2310
							} else {
2311
								data.warning && elf.error(data.warning);
2312
								data.removed && data.removed.length && elf.remove(data);
2313
								data.added   && data.added.length   && elf.add(data);
2314
								data.changed && data.changed.length && elf.change(data);';
2315
				if ($bind) {
2316
					$script .= '
2317
								elf.trigger(\''.$bind.'\', data);';
2318
				}
2319
				$script .= '
2320
								data.sync && elf.sync();
2321
							}
2322
						}
2323
					} catch(e) {
2324
						// for CORS
2325
						w.postMessage && w.postMessage(JSON.stringify({bind:\''.$bind.'\',data:'.$json.'}), \'*\');
2326
					}';
2327
			}
2328
			$script .= 'window.close();';
2329
			
2330
			$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>';
2331
			
2332
			header('Content-Type: text/html; charset=utf-8');
2333
			header('Content-Length: '.strlen($out));
2334
			header('Cache-Control: private');
2335
			header('Pragma: no-cache');
2336
			
2337
			echo $out;
2338
			
2339
		} else {
2340
			$url = $this->callbackWindowURL;
2341
			$url .= ((strpos($url, '?') === false)? '?' : '&')
2342
				 . '&node=' . rawurlencode($node)
2343
				 . (($json !== '{}')? ('&json=' . rawurlencode($json)) : '')
2344
				 . ($bind? ('&bind=' .  rawurlencode($bind)) : '')
2345
				 . '&done=1';
2346
			
2347
			header('Location: ' . $url);
2348
			
2349
		}
2350
		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...
2351
	}
2352
2353
	/***************************************************************************/
2354
	/*                                   utils                                 */
2355
	/***************************************************************************/
2356
	
2357
	/**
2358
	 * Return root - file's owner
2359
	 *
2360
	 * @param  string  file hash
2361
	 * @return elFinderStorageDriver
2362
	 * @author Dmitry (dio) Levashov
2363
	 **/
2364
	protected function volume($hash) {
2365
		foreach ($this->volumes as $id => $v) {
2366
			if (strpos(''.$hash, $id) === 0) {
2367
				return $this->volumes[$id];
2368
			} 
2369
		}
2370
		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...
2371
	}
2372
	
2373
	/**
2374
	 * Return files info array 
2375
	 *
2376
	 * @param  array  $data  one file info or files info
2377
	 * @return array
2378
	 * @author Dmitry (dio) Levashov
2379
	 **/
2380
	protected function toArray($data) {
2381
		return isset($data['hash']) || !is_array($data) ? array($data) : $data;
2382
	}
2383
	
2384
	/**
2385
	 * Return fils hashes list
2386
	 *
2387
	 * @param  array  $files  files info
2388
	 * @return array
2389
	 * @author Dmitry (dio) Levashov
2390
	 **/
2391
	protected function hashes($files) {
2392
		$ret = array();
2393
		foreach ($files as $file) {
2394
			$ret[] = $file['hash'];
2395
		}
2396
		return $ret;
2397
	}
2398
	
2399
	/**
2400
	 * Remove from files list hidden files and files with required mime types
2401
	 *
2402
	 * @param  array  $files  files info
2403
	 * @return array
2404
	 * @author Dmitry (dio) Levashov
2405
	 **/
2406
	protected function filter($files) {
2407
		foreach ($files as $i => $file) {
2408
			if (!empty($file['hidden']) || !$this->default->mimeAccepted($file['mime'])) {
2409
				unset($files[$i]);
2410
			}
2411
		}
2412
		return array_merge($files, array());
2413
	}
2414
	
2415
	protected function utime() {
2416
		$time = explode(" ", microtime());
2417
		return (double)$time[1] + (double)$time[0];
2418
	}
2419
	
2420
	
2421
	/***************************************************************************/
2422
	/*                           static  utils                                 */
2423
	/***************************************************************************/
2424
	
2425
	/**
2426
	 * Return Is Animation Gif
2427
	 * 
2428
	 * @param  string $path server local path of target image
2429
	 * @return bool
2430
	 */
2431
	public static function isAnimationGif($path) {
2432
		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...
2433
		switch ($type) {
2434
			case IMAGETYPE_GIF:
2435
				break;
2436
			default:
2437
				return false;
2438
		}
2439
	
2440
		$imgcnt = 0;
2441
		$fp = fopen($path, 'rb');
2442
		@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...
2443
		$c = @fread($fp,1);
2444
		if (ord($c) != 0x39) {  // GIF89a
2445
			return false;
2446
		}
2447
	
2448
		while (!feof($fp)) {
2449
			do {
2450
				$c = fread($fp, 1);
2451
			} while(ord($c) != 0x21 && !feof($fp));
2452
	
2453
			if (feof($fp)) {
2454
				break;
2455
			}
2456
	
2457
			$c2 = fread($fp,2);
2458
			if (bin2hex($c2) == "f904") {
2459
				$imgcnt++;
2460
			}
2461
	
2462
			if (feof($fp)) {
2463
				break;
2464
			}
2465
		}
2466
	
2467
		if ($imgcnt > 1) {
2468
			return true;
2469
		} else {
2470
			return false;
2471
		}
2472
	}
2473
2474
	/**
2475
	 * Return Is seekable stream resource
2476
	 * 
2477
	 * @param resource $resource
2478
	 * @return bool
2479
	 */
2480
	public static function isSeekableStream($resource) {
2481
		$metadata = stream_get_meta_data($resource);
2482
		return $metadata['seekable'];
2483
	}
2484
2485
	/**
2486
	 * serialize and base64_encode of session data (If needed)
2487
	 * 
2488
	 * @param  mixed $var  target variable
2489
	 * @author Naoki Sawada
2490
	 */
2491
	public static function sessionDataEncode($var) {
2492
		if (self::$base64encodeSessionData) {
2493
			$var = base64_encode(serialize($var));
2494
		}
2495
		return $var;
2496
	}
2497
	
2498
	/**
2499
	 * base64_decode and unserialize of session data  (If needed)
2500
	 * 
2501
	 * @param  mixed $var      target variable
2502
	 * @param  bool  $checkIs  data type for check (array|string|object|int)
2503
	 * @author Naoki Sawada
2504
	 */
2505
	public static function sessionDataDecode(&$var, $checkIs = null) {
2506
		if (self::$base64encodeSessionData) {
2507
			$data = @unserialize(@base64_decode($var));
2508
		} else {
2509
			$data = $var;
2510
		}
2511
		$chk = true;
2512
		if ($checkIs) {
2513
			switch ($checkIs) {
2514
				case 'array':
2515
					$chk = is_array($data);
2516
					break;
2517
				case 'string':
2518
					$chk = is_string($data);
2519
					break;
2520
				case 'object':
2521
					$chk = is_object($data);
2522
					break;
2523
				case 'int':
2524
					$chk = is_int($data);
2525
					break;
2526
			}
2527
		}
2528
		if (!$chk) {
2529
			unset($var);
2530
			return false;
2531
		}
2532
		return $data;
2533
	}
2534
} // END class
2535