Issues (661)

Security Analysis    not enabled

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

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

php/elFinder.class.php (58 issues)

Upgrade to new PHP Analysis Engine

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

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

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

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

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

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

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

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

Let’s take a look at a few examples:

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

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


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

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

Logical Operators are used for Control-Flow

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

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

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

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

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

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

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

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
417
					list(, $sub) = array_pad(explode('.', $_cmd), 2, '');
418
					if ($sub) {
419
						$sub = str_replace('\'', '\\\'', $sub);
420
						$addSub = create_function('$cmd', 'return $cmd . \'.\' . trim(\'' . $sub . '\');');
0 ignored issues
show
Security Best Practice introduced by
The use of create_function is highly discouraged, better use a closure.

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

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

// Better use
$function = function($a, $b) { return $a + $b; }
Loading history...
421
						$cmds = array_merge($cmds, array_map($addSub, $allCmds));
422
					} else {
423
						$cmds = array_merge($cmds, $allCmds);
424
					}
425
				} else {
426
					$cmds[] = $_cmd;
427
				}
428
			}
429
		}
430
		$cmds = array_unique($cmds);
431
		
432
		foreach ($cmds as $cmd) {
433
			if (!isset($this->listeners[$cmd])) {
434
				$this->listeners[$cmd] = array();
435
			}
436
437
			if (is_callable($handler)) {
438
				$this->listeners[$cmd][] = $handler;
439
			}
440
		}
441
442
		return $this;
443
	}
444
	
445
	/**
446
	 * Remove event (command exec) handler
447
	 *
448
	 * @param  string  command name
449
	 * @param  string|array  callback name or array(object, method)
450
	 * @return elFinder
451
	 * @author Dmitry (dio) Levashov
452
	 **/
453
	public function unbind($cmd, $handler) {
454
		if (!empty($this->listeners[$cmd])) {
455
			foreach ($this->listeners[$cmd] as $i => $h) {
456
				if ($h === $handler) {
457
					unset($this->listeners[$cmd][$i]);
458
					return $this;
459
				}
460
			}
461
		}
462
		return $this;
463
	}
464
	
465
	/**
466
	 * Return true if command exists
467
	 *
468
	 * @param  string  command name
469
	 * @return bool
470
	 * @author Dmitry (dio) Levashov
471
	 **/
472
	public function commandExists($cmd) {
473
		return $this->loaded && isset($this->commands[$cmd]) && method_exists($this, $cmd);
474
	}
475
	
476
	/**
477
	 * Return root - file's owner (public func of volume())
478
	 *
479
	 * @param  string  file hash
480
	 * @return elFinderStorageDriver
481
	 * @author Naoki Sawada
482
	 */
483
	public function getVolume($hash) {
484
		return $this->volume($hash);
485
	}
486
	
487
	/**
488
	 * Return command required arguments info
489
	 *
490
	 * @param  string  command name
491
	 * @return array
492
	 * @author Dmitry (dio) Levashov
493
	 **/
494
	public function commandArgsList($cmd) {
495
		return $this->commandExists($cmd) ? $this->commands[$cmd] : array();
496
	}
497
498
	private function session_expires() {
499
		
500
		if (!isset($_SESSION[self::$sessionCacheKey . ':LAST_ACTIVITY'])) {
501
			$_SESSION[self::$sessionCacheKey . ':LAST_ACTIVITY'] = time();
502
			return false;
503
		}
504
505
		if ( ($this->timeout > 0) && (time() - $_SESSION[self::$sessionCacheKey . ':LAST_ACTIVITY'] > $this->timeout) ) {
506
			return true;
507
		}
508
509
		$_SESSION[self::$sessionCacheKey . ':LAST_ACTIVITY'] = time();
510
		return false;	
511
	}
512
	
513
	/**
514
	 * Exec command and return result
515
	 *
516
	 * @param  string  $cmd  command name
517
	 * @param  array   $args command arguments
518
	 * @return array
519
	 * @author Dmitry (dio) Levashov
520
	 **/
521
	public function exec($cmd, $args) {
522
		
523
		if (!$this->loaded) {
524
			return array('error' => $this->error(self::ERROR_CONF, self::ERROR_CONF_NO_VOL));
525
		}
526
527
		if ($this->session_expires()) {
528
			return array('error' => $this->error(self::ERROR_SESSION_EXPIRES));
529
		}
530
		
531
		if (!$this->commandExists($cmd)) {
532
			return array('error' => $this->error(self::ERROR_UNKNOWN_CMD));
533
		}
534
		
535
		if (!empty($args['mimes']) && is_array($args['mimes'])) {
536
			foreach ($this->volumes as $id => $v) {
537
				$this->volumes[$id]->setMimesFilter($args['mimes']);
538
			}
539
		}
540
541
		// call pre handlers for this command
542
		$args['sessionCloseEarlier'] = isset($this->sessionUseCmds[$cmd])? false : $this->sessionCloseEarlier;
543
		if (!empty($this->listeners[$cmd.'.pre'])) {
544
			$volume = isset($args['target'])? $this->volume($args['target']) : false;
545 View Code Duplication
			foreach ($this->listeners[$cmd.'.pre'] as $handler) {
0 ignored issues
show
This code seems to be duplicated across your project.

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

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

Loading history...
546
				call_user_func_array($handler, array($cmd, &$args, $this, $volume));
547
			}
548
		}
549
		
550
		// unlock session data for multiple access
551
		$this->sessionCloseEarlier && $args['sessionCloseEarlier'] && session_id() && session_write_close();
552
		
553
		if (substr(PHP_OS,0,3) === 'WIN') {
554
			// set time out
555
			if (($_max_execution_time = ini_get('max_execution_time')) && $_max_execution_time < 300) {
556
				@set_time_limit(300);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
557
			}
558
		}
559
		
560
		$result = $this->$cmd($args);
561
		
562
		if (isset($result['removed'])) {
563
			foreach ($this->volumes as $volume) {
564
				$result['removed'] = array_merge($result['removed'], $volume->removed());
565
				$volume->resetRemoved();
566
			}
567
		}
568
		
569
		// call handlers for this command
570
		if (!empty($this->listeners[$cmd])) {
571
			foreach ($this->listeners[$cmd] as $handler) {
572
				if (call_user_func_array($handler,array($cmd,&$result,$args,$this))) {
573
					// handler return true to force sync client after command completed
574
					$result['sync'] = true;
575
				}
576
			}
577
		}
578
		
579
		// replace removed files info with removed files hashes
580
		if (!empty($result['removed'])) {
581
			$removed = array();
582
			foreach ($result['removed'] as $file) {
583
				$removed[] = $file['hash'];
584
			}
585
			$result['removed'] = array_unique($removed);
586
		}
587
		// remove hidden files and filter files by mimetypes
588
		if (!empty($result['added'])) {
589
			$result['added'] = $this->filter($result['added']);
590
		}
591
		// remove hidden files and filter files by mimetypes
592
		if (!empty($result['changed'])) {
593
			$result['changed'] = $this->filter($result['changed']);
594
		}
595
		
596
		if ($this->debug || !empty($args['debug'])) {
597
			$result['debug'] = array(
598
				'connector' => 'php', 
599
				'phpver'    => PHP_VERSION,
600
				'time'      => $this->utime() - $this->time,
601
				'memory'    => (function_exists('memory_get_peak_usage') ? ceil(memory_get_peak_usage()/1024).'Kb / ' : '').ceil(memory_get_usage()/1024).'Kb / '.ini_get('memory_limit'),
602
				'upload'    => $this->uploadDebug,
603
				'volumes'   => array(),
604
				'mountErrors' => $this->mountErrors
605
				);
606
			
607
			foreach ($this->volumes as $id => $volume) {
608
				$result['debug']['volumes'][] = $volume->debug();
609
			}
610
		}
611
		
612
		foreach ($this->volumes as $volume) {
613
			$volume->umount();
614
		}
615
		
616
		if (!empty($result['callback'])) {
617
			$result['callback']['json'] = json_encode($result);
618
			$this->callback($result['callback']);
619
		} else {
620
			return $result;
621
		}
622
	}
623
	
624
	/**
625
	 * Return file real path
626
	 *
627
	 * @param  string  $hash  file hash
628
	 * @return string
629
	 * @author Dmitry (dio) Levashov
630
	 **/
631
	public function realpath($hash)	{
632
		if (($volume = $this->volume($hash)) == false) {
633
			return false;
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() {
645
		if (isset($_SESSION[$this->netVolumesSessionKey])) {
646
			if ($data = elFinder::sessionDataDecode($_SESSION[$this->netVolumesSessionKey], 'array')) {
0 ignored issues
show
'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) {
661
		$_SESSION[$this->netVolumesSessionKey] = elFinder::sessionDataEncode($volumes);
662
	}
663
664
	/**
665
	 * Remove netmount volume
666
	 * 
667
	 * @param string $key  netvolume key
668
	 */
669
	protected function removeNetVolume($key) {
670
		$netVolumes = $this->getNetVolumes();
671
		if (is_string($key) && isset($netVolumes[$key])) {
672
			unset($netVolumes[$key]);
673
			$this->saveNetVolumes($netVolumes);
674
		}
675
	}
676
677
	/**
678
	 * Get plugin instance & set to $this->plugins
679
	 * 
680
	 * @param  string $name   Plugin name (dirctory name)
681
	 * @param  array  $opts   Plugin options (optional)
682
	 * @return object | bool Plugin object instance Or false
683
	 * @author Naoki Sawada
684
	 */
685
	protected function getPluginInstance($name, $opts = array()) {
686
		$key = strtolower($name);
687
		if (! isset($this->plugins[$key])) {
688
			$p_file = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . $name . DIRECTORY_SEPARATOR . 'plugin.php';
689
			if (is_file($p_file)) {
690
				require_once $p_file;
691
				$class = 'elFinderPlugin' . $name;
692
				$this->plugins[$key] = new $class($opts);
693
			} else {
694
				$this->plugins[$key] = false;
695
			}
696
		}
697
		return $this->plugins[$key];
698
	}
699
700
	/***************************************************************************/
701
	/*                                 commands                                */
702
	/***************************************************************************/
703
	
704
	/**
705
	 * Normalize error messages
706
	 *
707
	 * @return array
708
	 * @author Dmitry (dio) Levashov
709
	 **/
710
	public function error() {
711
		$errors = array();
712
713
		foreach (func_get_args() as $msg) {
714
			if (is_array($msg)) {
715
				$errors = array_merge($errors, $msg);
716
			} else {
717
				$errors[] = $msg;
718
			}
719
		}
720
		
721
		return count($errors) ? $errors : array(self::ERROR_UNKNOWN);
722
	}
723
	
724
	protected function netmount($args) {
725
		// try session restart
726
		@session_start();
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...
727
		
728
		$options  = array();
729
		$protocol = $args['protocol'];
730
		
731
		if ($protocol === 'netunmount') {
732
			$key = $args['host'];
733
			$netVolumes = $this->getNetVolumes();
734
			if ($netVolumes[$key]) {
735
				$res = true;
736
				$volume = $this->volume($args['user']);
737
				if (method_exists($volume, 'netunmount')) {
738
					$res = $volume->netunmount($netVolumes, $key);
739
				}
740
				if ($res) {
741
					unset($netVolumes[$key]);
742
					$this->saveNetVolumes($netVolumes);
743
					return array('sync' => true);
744
				}
745
			}
746
			return array('error' => $this->error(self::ERROR_NETUNMOUNT));
747
		}
748
		
749
		$driver   = isset(self::$netDrivers[$protocol]) ? self::$netDrivers[$protocol] : '';
750
		$class    = 'elfindervolume'.$driver;
751
752
		if (!class_exists($class)) {
753
			return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], self::ERROR_NETMOUNT_NO_DRIVER));
754
		}
755
756
		if (!$args['path']) {
757
			$args['path'] = '/';
758
		}
759
760
		foreach ($args as $k => $v) {
761
			if ($k != 'options' && $k != 'protocol' && $v) {
762
				$options[$k] = $v;
763
			}
764
		}
765
766
		if (is_array($args['options'])) {
767
			foreach ($args['options'] as $key => $value) {
768
				$options[$key] = $value;
769
			}
770
		}
771
772
		$volume = new $class();
773
		
774
		if (method_exists($volume, 'netmountPrepare')) {
775
			$options = $volume->netmountPrepare($options);
776
			if (isset($options['exit'])) {
777
				if ($options['exit'] === 'callback') {
778
					$this->callback($options['out']);
779
				}
780
				return $options;
781
			}
782
		}
783
		
784
		$netVolumes = $this->getNetVolumes();
785
		if ($volume->mount($options)) {
786
			if (! $key = @ $volume->netMountKey) {
787
				$key = md5($protocol . '-' . join('-', $options));
788
			}
789
			$options['driver'] = $driver;
790
			$options['netkey'] = $key;
791
			$netVolumes[$key]  = $options;
792
			$this->saveNetVolumes($netVolumes);
793
			$rootstat = $volume->file($volume->root());
794
			$rootstat['netkey'] = $key;
795
			return array('added' => array($rootstat));
796
		} else {
797
			$this->removeNetVolume($volume);
0 ignored issues
show
$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...
798
			return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], implode(' ', $volume->error())));
799
		}
800
801
	}
802
803
	/**
804
	 * "Open" directory
805
	 * Return array with following elements
806
	 *  - cwd          - opened dir info
807
	 *  - files        - opened dir content [and dirs tree if $args[tree]]
808
	 *  - api          - api version (if $args[init])
809
	 *  - uplMaxSize   - if $args[init]
810
	 *  - error        - on failed
811
	 *
812
	 * @param  array  command arguments
813
	 * @return array
814
	 * @author Dmitry (dio) Levashov
815
	 **/
816
	protected function open($args) {
817
		$target = $args['target'];
818
		$init   = !empty($args['init']);
819
		$tree   = !empty($args['tree']);
820
		$volume = $this->volume($target);
821
		$cwd    = $volume ? $volume->dir($target) : false;
822
		$hash   = $init ? 'default folder' : '#'.$target;
823
		$sleep  = 0;
0 ignored issues
show
$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...
824
		$compare = '';
825
826
		// on init request we can get invalid dir hash -
827
		// dir which can not be opened now, but remembered by client,
828
		// so open default dir
829
		if ((!$cwd || !$cwd['read']) && $init) {
830
			$volume = $this->default;
831
			$cwd    = $volume->dir($volume->defaultPath());
832
		}
833
		
834
		if (!$cwd) {
835
			return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_DIR_NOT_FOUND));
836
		}
837
		if (!$cwd['read']) {
838
			return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_PERM_DENIED));
839
		}
840
841
		$files = array();
842
843
		// get other volume root
844
		if ($tree) {
845
			foreach ($this->volumes as $id => $v) {
846
				$files[] = $v->file($v->root());
847
			}
848
		}
849
850
		// get current working directory files list and add to $files if not exists in it
851 View Code Duplication
		if (($ls = $volume->scandir($cwd['hash'])) === false) {
852
			return array('error' => $this->error(self::ERROR_OPEN, $cwd['name'], $volume->error()));
853
		}
854
		// long polling mode
855
		if ($args['compare']) {
856
			$sleep = max(1, (int)$volume->getOption('lsPlSleep'));
857
			$limit = max(1, (int)$volume->getOption('plStandby') / $sleep) + 1;
858
			$timelimit = ini_get('max_execution_time');
859
			$compare = $args['compare'];
0 ignored issues
show
$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...
860
			do {
861
				$timelimit && @ set_time_limit($timelimit + $sleep);
862
				$_mtime = 0;
863
				foreach($ls as $_f) {
864
					$_mtime = max($_mtime, $_f['ts']);
865
				}
866
				$compare = strval(count($ls)).':'.strval($_mtime);
867
				if ($compare !== $args['compare']) {
868
					break;
869
				}
870
				if (--$limit) {
871
					sleep($sleep);
872
					$volume->clearstatcache();
873
					if (($ls = $volume->scandir($cwd['hash'])) === false) {
874
						break;
875
					}
876
				}
877
			} while($limit);
878 View Code Duplication
			if ($ls === false) {
0 ignored issues
show
This code seems to be duplicated across your project.

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

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

Loading history...
879
				return array('error' => $this->error(self::ERROR_OPEN, $cwd['name'], $volume->error()));
880
			}
881
		}
882
		
883
		if ($ls) {
884
			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...
885
				$files = array_merge($files, $ls);
886
				$files = array_unique($files, SORT_REGULAR);
887
			} else {
888
				$files = $ls;
889
			}
890
		}
891
		
892
		$result = array(
893
			'cwd'     => $cwd,
894
			'options' => $volume->options($cwd['hash']),
895
			'files'   => $files
896
		);
897
		
898
		if ($compare) {
899
			$result['cwd']['compare'] = $compare;
900
		}
901
		
902
		if (!empty($args['init'])) {
903
			$result['api'] = $this->version;
904
			$result['uplMaxSize'] = ini_get('upload_max_filesize');
905
			$result['uplMaxFile'] = ini_get('max_file_uploads');
906
			$result['netDrivers'] = array_keys(self::$netDrivers);
907
			if ($volume) {
908
				$result['cwd']['root'] = $volume->root();
909
			}
910
		}
911
		
912
		return $result;
913
	}
914
	
915
	/**
916
	 * Return dir files names list
917
	 *
918
	 * @param  array  command arguments
919
	 * @return array
920
	 * @author Dmitry (dio) Levashov
921
	 **/
922 View Code Duplication
	protected function ls($args) {
923
		$target = $args['target'];
924
		
925
		if (($volume = $this->volume($target)) == false
926
		|| ($list = $volume->ls($target)) === false) {
927
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
928
		}
929
		return array('list' => $list);
930
	}
931
	
932
	/**
933
	 * Return subdirs for required directory
934
	 *
935
	 * @param  array  command arguments
936
	 * @return array
937
	 * @author Dmitry (dio) Levashov
938
	 **/
939 View Code Duplication
	protected function tree($args) {
940
		$target = $args['target'];
941
		
942
		if (($volume = $this->volume($target)) == false
943
		|| ($tree = $volume->tree($target)) == false) {
944
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
945
		}
946
947
		return array('tree' => $tree);
948
	}
949
	
950
	/**
951
	 * Return parents dir for required directory
952
	 *
953
	 * @param  array  command arguments
954
	 * @return array
955
	 * @author Dmitry (dio) Levashov
956
	 **/
957 View Code Duplication
	protected function parents($args) {
958
		$target = $args['target'];
959
		
960
		if (($volume = $this->volume($target)) == false
961
		|| ($tree = $volume->parents($target)) == false) {
962
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
963
		}
964
965
		return array('tree' => $tree);
966
	}
967
	
968
	/**
969
	 * Return new created thumbnails list
970
	 *
971
	 * @param  array  command arguments
972
	 * @return array
973
	 * @author Dmitry (dio) Levashov
974
	 **/
975
	protected function tmb($args) {
976
		
977
		$result  = array('images' => array());
978
		$targets = $args['targets'];
979
		
980
		foreach ($targets as $target) {
981
			if (($volume = $this->volume($target)) != false
982
			&& (($tmb = $volume->tmb($target)) != false)) {
983
				$result['images'][$target] = $tmb;
984
			}
985
		}
986
		return $result;
987
	}
988
	
989
	/**
990
	 * Required to output file in browser when volume URL is not set 
991
	 * Return array contains opened file pointer, root itself and required headers
992
	 *
993
	 * @param  array  command arguments
994
	 * @return array
995
	 * @author Dmitry (dio) Levashov
996
	 **/
997
	protected function file($args) {
998
		$target   = $args['target'];
999
		$download = !empty($args['download']);
1000
		$h403     = 'HTTP/1.x 403 Access Denied';
1001
		$h404     = 'HTTP/1.x 404 Not Found';
1002
1003 View Code Duplication
		if (($volume = $this->volume($target)) == false) { 
1004
			return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
1005
		}
1006
		
1007 View Code Duplication
		if (($file = $volume->file($target)) == false) {
1008
			return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
1009
		}
1010
		
1011
		if (!$file['read']) {
1012
			return array('error' => 'Access denied', 'header' => $h403, 'raw' => true);
1013
		}
1014
		
1015 View Code Duplication
		if (($fp = $volume->open($target)) == false) {
1016
			return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
1017
		}
1018
1019
		// allow change MIME type by 'file.pre' callback functions
1020
		$mime = isset($args['mime'])? $args['mime'] : $file['mime'];
1021
		if ($download) {
1022
			$disp = 'attachment';
1023
		} else {
1024
			$dispInlineRegex = $volume->getOption('dispInlineRegex');
1025
			$inlineRegex = false;
1026
			if ($dispInlineRegex) {
1027
				$inlineRegex = '#' . str_replace('#', '\\#', $dispInlineRegex) . '#';
1028
				try {
1029
					preg_match($inlineRegex, '');
1030
				} catch(Exception $e) {
1031
					$inlineRegex = false;
1032
				}
1033
			}
1034
			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...
1035
				$inlineRegex = '#^(?:(?:image|text)|application/x-shockwave-flash$)#';
1036
			}
1037
			$disp  = preg_match($inlineRegex, $mime)? 'inline' : 'attachment';
1038
		}
1039
		
1040
		$filenameEncoded = rawurlencode($file['name']);
1041
		if (strpos($filenameEncoded, '%') === false) { // ASCII only
1042
			$filename = 'filename="'.$file['name'].'"';
1043
		} else {
1044
			$ua = $_SERVER['HTTP_USER_AGENT'];
1045
			if (preg_match('/MSIE [4-8]/', $ua)) { // IE < 9 do not support RFC 6266 (RFC 2231/RFC 5987)
1046
				$filename = 'filename="'.$filenameEncoded.'"';
1047
			} elseif (strpos($ua, 'Chrome') === false && strpos($ua, 'Safari') !== false && preg_match('#Version/[3-5]#', $ua)) { // Safari < 6
1048
				$filename = 'filename="'.str_replace('"', '', $file['name']).'"';
1049
			} else { // RFC 6266 (RFC 2231/RFC 5987)
1050
				$filename = 'filename*=UTF-8\'\''.$filenameEncoded;
1051
			}
1052
		}
1053
		
1054
		$result = array(
1055
			'volume'  => $volume,
1056
			'pointer' => $fp,
1057
			'info'    => $file,
1058
			'header'  => array(
1059
				'Content-Type: '.$mime, 
1060
				'Content-Disposition: '.$disp.'; '.$filename,
1061
				'Content-Transfer-Encoding: binary',
1062
				'Content-Length: '.$file['size'],
1063
				'Connection: close'
1064
			)
1065
		);
1066
		if (isset($file['url']) && $file['url'] && $file['url'] != 1) {
1067
			$result['header'][] = 'Content-Location: '.$file['url'];
1068
		}
1069
		return $result;
1070
	}
1071
	
1072
	/**
1073
	 * Count total files size
1074
	 *
1075
	 * @param  array  command arguments
1076
	 * @return array
1077
	 * @author Dmitry (dio) Levashov
1078
	 **/
1079
	protected function size($args) {
1080
		$size = 0;
1081
		
1082
		foreach ($args['targets'] as $target) {
1083
			if (($volume = $this->volume($target)) == false
1084
			|| ($file = $volume->file($target)) == false
1085
			|| !$file['read']) {
1086
				return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
1087
			}
1088
			
1089
			$size += $volume->size($target);
1090
		}
1091
		return array('size' => $size);
1092
	}
1093
	
1094
	/**
1095
	 * Create directory
1096
	 *
1097
	 * @param  array  command arguments
1098
	 * @return array
1099
	 * @author Dmitry (dio) Levashov
1100
	 **/
1101 View Code Duplication
	protected function mkdir($args) {
1102
		$target = $args['target'];
1103
		$name   = $args['name'];
1104
		
1105
		if (($volume = $this->volume($target)) == false) {
1106
			return array('error' => $this->error(self::ERROR_MKDIR, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target));
1107
		}
1108
1109
		return ($dir = $volume->mkdir($target, $name)) == false
1110
			? array('error' => $this->error(self::ERROR_MKDIR, $name, $volume->error()))
1111
			: array('added' => array($dir));
1112
	}
1113
	
1114
	/**
1115
	 * Create empty file
1116
	 *
1117
	 * @param  array  command arguments
1118
	 * @return array
1119
	 * @author Dmitry (dio) Levashov
1120
	 **/
1121 View Code Duplication
	protected function mkfile($args) {
1122
		$target = $args['target'];
1123
		$name   = $args['name'];
1124
		
1125
		if (($volume = $this->volume($target)) == false) {
1126
			return array('error' => $this->error(self::ERROR_MKFILE, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target));
1127
		}
1128
1129
		return ($file = $volume->mkfile($target, $args['name'])) == false
1130
			? array('error' => $this->error(self::ERROR_MKFILE, $name, $volume->error()))
1131
			: array('added' => array($file));
1132
	}
1133
	
1134
	/**
1135
	 * Rename file
1136
	 *
1137
	 * @param  array  $args
1138
	 * @return array
1139
	 * @author Dmitry (dio) Levashov
1140
	 **/
1141
	protected function rename($args) {
1142
		$target = $args['target'];
1143
		$name   = $args['name'];
1144
		
1145 View Code Duplication
		if (($volume = $this->volume($target)) == false
1146
		||  ($rm  = $volume->file($target)) == false) {
1147
			return array('error' => $this->error(self::ERROR_RENAME, '#'.$target, self::ERROR_FILE_NOT_FOUND));
1148
		}
1149
		$rm['realpath'] = $volume->realpath($target);
1150
		
1151
		return ($file = $volume->rename($target, $name)) == false
1152
			? array('error' => $this->error(self::ERROR_RENAME, $rm['name'], $volume->error()))
1153
			: array('added' => array($file), 'removed' => array($rm));
1154
	}
1155
	
1156
	/**
1157
	 * Duplicate file - create copy with "copy %d" suffix
1158
	 *
1159
	 * @param array  $args  command arguments
1160
	 * @return array
1161
	 * @author Dmitry (dio) Levashov
1162
	 **/
1163
	protected function duplicate($args) {
1164
		$targets = is_array($args['targets']) ? $args['targets'] : array();
1165
		$result  = array('added' => array());
1166
		$suffix  = empty($args['suffix']) ? 'copy' : $args['suffix'];
1167
		
1168
		foreach ($targets as $target) {
1169 View Code Duplication
			if (($volume = $this->volume($target)) == false
1170
			|| ($src = $volume->file($target)) == false) {
1171
				$result['warning'] = $this->error(self::ERROR_COPY, '#'.$target, self::ERROR_FILE_NOT_FOUND);
1172
				break;
1173
			}
1174
			
1175
			if (($file = $volume->duplicate($target, $suffix)) == false) {
1176
				$result['warning'] = $this->error($volume->error());
1177
				break;
1178
			}
1179
			
1180
			$result['added'][] = $file;
1181
		}
1182
		
1183
		return $result;
1184
	}
1185
		
1186
	/**
1187
	 * Remove dirs/files
1188
	 *
1189
	 * @param array  command arguments
1190
	 * @return array
1191
	 * @author Dmitry (dio) Levashov
1192
	 **/
1193
	protected function rm($args) {
1194
		$targets = is_array($args['targets']) ? $args['targets'] : array();
1195
		$result  = array('removed' => array());
1196
		
1197
		foreach ($targets as $target) {
1198 View Code Duplication
			if (($volume = $this->volume($target)) == false) {
1199
				$result['warning'] = $this->error(self::ERROR_RM, '#'.$target, self::ERROR_FILE_NOT_FOUND);
1200
				return $result;
1201
			}
1202
			if (!$volume->rm($target)) {
1203
				$result['warning'] = $this->error($volume->error());
1204
				return $result;
1205
			}
1206
		}
1207
1208
		return $result;
1209
	}
1210
1211
	/**
1212
	* Get remote contents
1213
	*
1214
	* @param  string   $url     target url
1215
	* @param  int      $timeout timeout (sec)
1216
	* @param  int      $redirect_max redirect max count
1217
	* @param  string   $ua
1218
	* @param  resource $fp
1219
	* @return string or bool(false)
1220
	* @retval string contents
1221
	* @retval false  error
1222
	* @author Naoki Sawada
1223
	**/
1224
	protected function get_remote_contents( &$url, $timeout = 30, $redirect_max = 5, $ua = 'Mozilla/5.0', $fp = null ) {
1225
		$method = (function_exists('curl_exec') && !ini_get('safe_mode'))? 'curl_get_contents' : 'fsock_get_contents'; 
1226
		return $this->$method( $url, $timeout, $redirect_max, $ua, $fp );
1227
	}
1228
	
1229
	/**
1230
	 * Get remote contents with cURL
1231
	 *
1232
	 * @param  string   $url     target url
1233
	 * @param  int      $timeout timeout (sec)
1234
	 * @param  int      $redirect_max redirect max count
1235
	 * @param  string   $ua
1236
	 * @param  resource $outfp
1237
	 * @return string or bool(false)
1238
	 * @retval string contents
1239
	 * @retval false  error
1240
	 * @author Naoki Sawada
1241
	 **/
1242
	 protected function curl_get_contents( &$url, $timeout, $redirect_max, $ua, $outfp ){
1243
		$ch = curl_init();
1244
		curl_setopt( $ch, CURLOPT_URL, $url );
1245
		curl_setopt( $ch, CURLOPT_HEADER, false );
1246
		if ($outfp) {
1247
			curl_setopt( $ch, CURLOPT_FILE, $outfp );
1248
		} else {
1249
			curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
1250
			curl_setopt( $ch, CURLOPT_BINARYTRANSFER, true );
1251
		}
1252
		curl_setopt( $ch, CURLOPT_LOW_SPEED_LIMIT, 1 );
1253
		curl_setopt( $ch, CURLOPT_LOW_SPEED_TIME, $timeout );
1254
		curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
1255
		curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1);
1256
		curl_setopt( $ch, CURLOPT_MAXREDIRS, $redirect_max);
1257
		curl_setopt( $ch, CURLOPT_USERAGENT, $ua);
1258
		$result = curl_exec( $ch );
1259
		$url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
1260
		curl_close( $ch );
1261
		return $outfp? $outfp : $result;
1262
	}
1263
	
1264
	/**
1265
	 * Get remote contents with fsockopen()
1266
	 *
1267
	 * @param  string   $url          url
1268
	 * @param  int      $timeout      timeout (sec)
1269
	 * @param  int      $redirect_max redirect max count
1270
	 * @param  string   $ua
1271
	 * @param  resource $outfp
1272
	 * @return string or bool(false)
1273
	 * @retval string contents
1274
	 * @retval false  error
1275
	 * @author Naoki Sawada
1276
	 */
1277
	protected function fsock_get_contents( &$url, $timeout, $redirect_max, $ua, $outfp ) {
1278
1279
		$connect_timeout = 3;
1280
		$connect_try = 3;
1281
		$method = 'GET';
1282
		$readsize = 4096;
1283
1284
		$getSize = null;
1285
		$headers = '';
1286
		
1287
		$arr = parse_url($url);
1288
		if (!$arr){
1289
			// Bad request
1290
			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...
1291
		}
1292
		
1293
		// query
1294
		$arr['query'] = isset($arr['query']) ? '?'.$arr['query'] : '';
1295
		// port
1296
		$arr['port'] = isset($arr['port']) ? $arr['port'] : (!empty($arr['https'])? 443 : 80);
1297
		
1298
		$url_base = $arr['scheme'].'://'.$arr['host'].':'.$arr['port'];
1299
		$url_path = isset($arr['path']) ? $arr['path'] : '/';
1300
		$uri = $url_path.$arr['query'];
1301
		
1302
		$query = $method.' '.$uri." HTTP/1.0\r\n";
1303
		$query .= "Host: ".$arr['host']."\r\n";
1304
		if (!empty($ua)) $query .= "User-Agent: ".$ua."\r\n";
1305
		if (!is_null($getSize)) $query .= 'Range: bytes=0-' . ($getSize - 1) . "\r\n";
1306
		
1307
		$query .= $headers;
1308
1309
		$query .= "\r\n";
1310
1311
		$fp = $connect_try_count = 0;
1312
		while( !$fp && $connect_try_count < $connect_try ) {
1313
	
1314
			$errno = 0;
1315
			$errstr = "";
1316
			$fp = @ fsockopen(
1317
			$arr['https'].$arr['host'],
1318
			$arr['port'],
1319
			$errno,$errstr,$connect_timeout);
1320
			if ($fp) break;
1321
			$connect_try_count++;
1322
			if (connection_aborted()) {
1323
				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...
1324
			}
1325
			sleep(1); // wait 1sec
1326
		}
1327
		
1328
		$fwrite = 0;
0 ignored issues
show
$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...
1329
		for ($written = 0; $written < strlen($query); $written += $fwrite) {
1330
			$fwrite = fwrite($fp, substr($query, $written));
1331
			if (!$fwrite) {
1332
				break;
1333
			}
1334
		}
1335
		
1336
		$response = '';
0 ignored issues
show
$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...
1337
		
1338
		if ($timeout) {
1339
			socket_set_timeout($fp, $timeout);
1340
		}
1341
		
1342
		$_response = '';
1343
		$header = '';
1344
		while($_response !== "\r\n"){
1345
			$_response = fgets($fp, $readsize);
1346
			$header .= $_response;
1347
		};
1348
		
1349
		$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...
1350
		$rc = (int)$rccd[1];
1351
		
1352
		// Redirect
1353
		switch ($rc) {
1354
			case 307: // Temporary Redirect
1355
			case 303: // See Other
1356
			case 302: // Moved Temporarily
1357
			case 301: // Moved Permanently
1358
				$matches = array();
1359
				if (preg_match('/^Location: (.+?)(#.+)?$/im',$header,$matches) && --$redirect_max > 0) {
1360
					$url = trim($matches[1]);
1361
					$hash = isset($matches[2])? trim($matches[2]) : '';
0 ignored issues
show
$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...
1362
					if (!preg_match('/^https?:\//',$url)) { // no scheme
1363
						if ($url{0} != '/') { // Relative path
1364
							// to Absolute path
1365
							$url = substr($url_path,0,strrpos($url_path,'/')).'/'.$url;
1366
						}
1367
						// add sheme,host
1368
						$url = $url_base.$url;
1369
					}
1370
					fclose($fp);
1371
					return $this->fsock_get_contents( $url, $timeout, $redirect_max, $ua, $outfp );
1372
				}
1373
		}
1374
		
1375
		$body = '';
1376
		if (!$outfp) {
1377
			$outfp = fopen('php://temp', 'rwb');
1378
			$body = true;
1379
		}
1380
		while(fwrite($outfp, fread($fp, $readsize))) {
1381
			if ($timeout) {
1382
				$_status = socket_get_status($fp);
1383
				if ($_status['timed_out']) {
1384
					fclose($outfp);
1385
					fclose($fp);
1386
					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...
1387
				}
1388
			}
1389
		}
1390
		if ($body) {
1391
			rewind($outfp);
1392
			$body = stream_get_contents($outfp);
1393
			fclose($outfp);
1394
			$outfp = null;
1395
		}
1396
		
1397
		fclose($fp);
1398
		
1399
		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 1399 which is incompatible with the return type documented by elFinder::fsock_get_contents of type string.
Loading history...
1400
	}
1401
	
1402
	/**
1403
	 * Parse Data URI scheme
1404
	 * 
1405
	 * @param  string $str
1406
	 * @param  array  $extTable
1407
	 * @return array
1408
	 * @author Naoki Sawada
1409
	 */
1410
	protected function parse_data_scheme( $str, $extTable ) {
1411
		$data = $name = '';
1412
		if ($fp = fopen('data://'.substr($str, 5), 'rb')) {
1413 View Code Duplication
			if ($data = stream_get_contents($fp)) {
0 ignored issues
show
This code seems to be duplicated across your project.

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

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

Loading history...
1414
				$meta = stream_get_meta_data($fp);
1415
				$ext = isset($extTable[$meta['mediatype']])? '.' . $extTable[$meta['mediatype']] : '';
1416
				$name = substr(md5($data), 0, 8) . $ext;
1417
			}
1418
			fclose($fp);
1419
		}
1420
		return array($data, $name);
1421
	}
1422
	
1423
	/**
1424
	 * Detect file type extension by local path
1425
	 * 
1426
	 * @param  string $path Local path
1427
	 * @return string file type extension with dot
1428
	 * @author Naoki Sawada
1429
	 */
1430
	protected function detectFileExtension($path) {
1431
		static $type, $finfo, $extTable;
1432
		if (!$type) {
1433
			$keys = array_keys($this->volumes);
1434
			$volume = $this->volumes[$keys[0]];
1435
			$extTable = array_flip(array_unique($volume->getMimeTable()));
1436
			
1437
			if (class_exists('finfo', false)) {
1438
				$tmpFileInfo = @explode(';', @finfo_file(finfo_open(FILEINFO_MIME), __FILE__));
1439
			} else {
1440
				$tmpFileInfo = false;
1441
			}
1442
			$regexp = '/text\/x\-(php|c\+\+)/';
1443
			if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) {
1444
				$type = 'finfo';
1445
				$finfo = finfo_open(FILEINFO_MIME);
1446
			} elseif (function_exists('mime_content_type')
1447
					&& preg_match($regexp, array_shift(explode(';', mime_content_type(__FILE__))))) {
0 ignored issues
show
explode(';', mime_content_type(__FILE__)) cannot be passed to array_shift() as the parameter $array expects a reference.
Loading history...
1448
				$type = 'mime_content_type';
1449
			} elseif (function_exists('getimagesize')) {
1450
				$type = 'getimagesize';
1451
			} else {
1452
				$type = 'none';
1453
			}
1454
		}
1455
		
1456
		$mime = '';
1457
		if ($type === 'finfo') {
1458
			$mime = @finfo_file($finfo, $path);
1459
		} elseif ($type === 'mime_content_type') {
1460
			$mime = mime_content_type($path);
1461
		} elseif ($type === 'getimagesize') {
1462
			if ($img = @getimagesize($path)) {
1463
				$mime = $img['mime'];
1464
			}
1465
		}
1466
		
1467
		if ($mime) {
1468
			$mime = explode(';', $mime);
1469
			$mime = trim($mime[0]);
1470
			
1471 View Code Duplication
			if (in_array($mime, array('application/x-empty', 'inode/x-empty'))) {
0 ignored issues
show
This code seems to be duplicated across your project.

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

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

Loading history...
1472
				// finfo return this mime for empty files
1473
				$mime = 'text/plain';
1474
			} elseif ($mime == 'application/x-zip') {
1475
				// http://elrte.org/redmine/issues/163
1476
				$mime = 'application/zip';
1477
			}
1478
		}
1479
		
1480
		return ($mime && isset($extTable[$mime]))? ('.' . $extTable[$mime]) : '';
1481
	}
1482
	
1483
	/**
1484
	 * Get temporary dirctroy path
1485
	 * 
1486
	 * @param  string $volumeTempPath
1487
	 * @return string
1488
	 * @author Naoki Sawada
1489
	 */
1490
	private function getTempDir($volumeTempPath = null) {
1491
		$testDirs = array();
1492
		if ($this->uploadTempPath) {
1493
			$testDirs[] = rtrim(realpath($this->uploadTempPath), DIRECTORY_SEPARATOR);
1494
		}
1495
		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...
1496
			$testDirs[] = rtrim(realpath($volumeTempPath), DIRECTORY_SEPARATOR);
1497
		}
1498
		if (function_exists('sys_get_temp_dir')) {
1499
			$testDirs[] = sys_get_temp_dir();
1500
		}
1501
		$tempDir = '';
1502
		foreach($testDirs as $testDir) {
1503
			if (!$testDir || !is_dir($testDir)) continue;
1504
			if (is_writable($testDir)) {
1505
				$tempDir = $testDir;
1506
				$gc = time() - 3600;
1507
				foreach(glob($tempDir . DIRECTORY_SEPARATOR .'ELF*') as $cf) {
1508
					if (filemtime($cf) < $gc) {
1509
						@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...
1510
					}
1511
				}
1512
				break;
1513
			}
1514
		}
1515
		return $tempDir;
1516
	}
1517
	
1518
	/**
1519
	 * chmod
1520
	 *
1521
	 * @param array  command arguments
1522
	 * @return array
1523
	 * @author David Bartle
1524
	 **/
1525
	protected function chmod($args) {
1526
		$targets = $args['targets'];
1527
		$mode    = intval((string)$args['mode'], 8);
1528
1529
		if (!is_array($targets)) {
1530
			$targets = array($targets);
1531
		}
1532
		
1533
		$result = array();
1534
		
1535 View Code Duplication
		if (($volume = $this->volume($targets[0])) == false) {
0 ignored issues
show
This code seems to be duplicated across your project.

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

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

Loading history...
1536
			$result['error'] = $this->error(self::ERROR_CONF_NO_VOL);
1537
			return $result;
1538
		}
1539
1540
		$files = array();
1541
		$errors = array();
1542
		foreach($targets as $target) {
1543
			$file = $volume->chmod($target, $mode);
1544
			if ($file) {
1545
				$files = array_merge($files, is_array($file)? $file : array($file));
1546
			} else {
1547
				$errors = array_merge($errors, $volume->error());
1548
			}
1549
		}
1550
		
1551
		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...
1552
			$result['changed'] = $files;
1553
			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...
1554
				$result['warning'] = $this->error($errors);
1555
			}
1556
		} else {
1557
			$result['error'] = $this->error($errors);
1558
		}
1559
		
1560
		return $result;
1561
	}
1562
	
1563
	/**
1564
	 * Check chunked upload files
1565
	 * 
1566
	 * @param string $tmpname  uploaded temporary file path
1567
	 * @param string $chunk    uploaded chunk file name
1568
	 * @param string $cid      uploaded chunked file id
1569
	 * @param string $tempDir  temporary dirctroy path
1570
	 * @return array (string JoinedTemporaryFilePath, string FileName) or (empty, empty)
1571
	 * @author Naoki Sawada
1572
	 */
1573
	private function checkChunkedFile($tmpname, $chunk, $cid, $tempDir, $volume = null) {
0 ignored issues
show
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...
1574
		if (preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m)) {
1575
			$fname = $m[1];
1576
			$encname = md5($cid . '_' . $fname);
1577
			$base = $tempDir . DIRECTORY_SEPARATOR . 'ELF' . $encname;
1578
			$clast = intval($m[3]);
1579
			if (is_null($tmpname)) {
1580
				ignore_user_abort(true);
1581
				sleep(10); // wait 10 sec
1582
				// chunked file upload fail
1583
				foreach(glob($base . '*') as $cf) {
1584
					@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...
1585
				}
1586
				ignore_user_abort(false);
1587
				return;
1588
			}
1589
			
1590
			$range = isset($_POST['range'])? trim($_POST['range']) : '';
1591
			if ($range && preg_match('/^(\d+),(\d+),(\d+)$/', $range, $ranges)) {
1592
				$start = $ranges[1];
1593
				$len   = $ranges[2];
1594
				$size  = $ranges[3];
1595
				$tmp = $base . '.part';
1596
				$csize = filesize($tmpname);
1597
				
1598
				$tmpExists = is_file($tmp);
1599
				if (!$tmpExists) {
1600
					// check upload max size
1601
					$uploadMaxSize = $volume->getUploadMaxSize();
1602
					if ($uploadMaxSize > 0 && $size > $uploadMaxSize) {
1603
						return array(self::ERROR_UPLOAD_FILE_SIZE, false);
1604
					}
1605
					// make temp file
1606
					$ok = false;
1607
					if ($fp = fopen($tmp, 'wb')) {
1608
						flock($fp, LOCK_EX);
1609
						$ok = ftruncate($fp, $size);
1610
						flock($fp, LOCK_UN);
1611
						fclose($fp);
1612
						touch($base);
1613
					}
1614
					if (!$ok) {
1615
						unlink($tmp);
1616
						return array(self::ERROR_UPLOAD_TEMP, false);
1617
					}
1618
				} else {
1619
					// wait until makeing temp file (for anothor session)
1620
					$cnt = 1200; // Time limit 120 sec
1621
					while(!is_file($base) && --$cnt) {
1622
						usleep(100000); // wait 100ms
1623
					}
1624
					if (!$cnt) {
1625
						return array(self::ERROR_UPLOAD_TEMP, false);
1626
					}
1627
				}
1628
				
1629
				// check size info
1630
				if ($len != $csize || $start + $len > $size || ($tmpExists && $size != filesize($tmp))) {
1631
					return array(self::ERROR_UPLOAD_TEMP, false);
1632
				}
1633
				
1634
				// write chunk data
1635
				$writelen = 0;
0 ignored issues
show
$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...
1636
				$src = fopen($tmpname, 'rb');
1637
				$fp = fopen($tmp, 'cb');
1638
				fseek($fp, $start);
1639
				$writelen = stream_copy_to_stream($src, $fp, $len);
1640
				fclose($fp);
1641
				fclose($src);
1642
				if ($writelen != $len) {
1643
					return array(self::ERROR_UPLOAD_TEMP, false);
1644
				}
1645
				
1646
				// write counts
1647
				file_put_contents($base, "\0", FILE_APPEND | LOCK_EX);
1648
				
1649
				if (filesize($base) >= $clast + 1) {
1650
					// Completion
1651
					unlink($base);
1652
					return array($tmp, $fname);
1653
				}
1654
			} else {
1655
				// old way
1656
				$part = $base . $m[2];
1657
				if (move_uploaded_file($tmpname, $part)) {
1658
					@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...
1659
					if ($clast < count(glob($base . '*'))) {
1660
						$parts = array();
1661
						for ($i = 0; $i <= $clast; $i++) {
1662
							$name = $base . '.' . $i . '_' . $clast;
1663
							if (is_readable($name)) {
1664
								$parts[] = $name;
1665
							} else {
1666
								$parts = null;
1667
								break;
1668
							}
1669
						}
1670
						if ($parts) {
1671
							if (!is_file($base)) {
1672
								touch($base);
1673
								if ($resfile = tempnam($tempDir, 'ELF')) {
1674
									$target = fopen($resfile, 'wb');
1675
									foreach($parts as $f) {
1676
										$fp = fopen($f, 'rb');
1677
										while (!feof($fp)) {
1678
											fwrite($target, fread($fp, 8192));
1679
										}
1680
										fclose($fp);
1681
										unlink($f);
1682
									}
1683
									fclose($target);
1684
									unlink($base);
1685
									return array($resfile, $fname);
1686
								}
1687
								unlink($base);
1688
							}
1689
						}
1690
					}
1691
				}
1692
			}
1693
		}
1694
		return array('', '');
1695
	}
1696
	
1697
	/**
1698
	 * Save uploaded files
1699
	 *
1700
	 * @param  array
1701
	 * @return array
1702
	 * @author Dmitry (dio) Levashov
1703
	 **/
1704
	protected function upload($args) {
1705
		$ngReg  = '/[\/\\?*:|"<>]/';
1706
		$target = $args['target'];
1707
		$volume = $this->volume($target);
1708
		$files  = isset($args['FILES']['upload']) && is_array($args['FILES']['upload']) ? $args['FILES']['upload'] : array();
1709
		$header = empty($args['html']) ? array() : array('header' => 'Content-Type: text/html; charset=utf-8');
1710
		$result = array_merge(array('added' => array()), $header);
1711
		$paths  = $args['upload_path']? $args['upload_path'] : array();
1712
		$chunk  = $args['chunk']? $args['chunk'] : '';
1713
		$cid    = $args['cid']? (int)$args['cid'] : '';
1714
		
1715
		$renames= array();
1716
		$suffix = '~';
1717
		if ($args['renames'] && is_array($args['renames'])) {
1718
			$renames = array_flip($args['renames']);
1719 View Code Duplication
			if (is_string($args['suffix']) && ! preg_match($ngReg, $args['suffix'])) {
0 ignored issues
show
This code seems to be duplicated across your project.

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

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

Loading history...
1720
				$suffix = $args['suffix'];
1721
			}
1722
		}
1723
		
1724
		if (!$volume) {
1725
			return array_merge(array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target)), $header);
1726
		}
1727
		
1728
		// regist Shutdown function
1729
		$GLOBALS['elFinderTempFiles'] = array();
1730
// 		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...
1731
// 			$shutdownfunc = function(){ // <- Parse error on PHP < 5.3 ;-(
1732
// 				foreach(array_keys($GLOBALS['elFinderTempFiles']) as $f){
1733
// 					@unlink($f);
1734
// 				}
1735
// 			};
1736
// 		} else {
1737
			$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...
1738
				foreach(array_keys($GLOBALS[\'elFinderTempFiles\']) as $f){
1739
					@unlink($f);
1740
				}
1741
			');
1742
//		}
1743
		register_shutdown_function($shutdownfunc);
1744
		
1745
		// file extentions table by MIME
1746
		$extTable = array_flip(array_unique($volume->getMimeTable()));
1747
		
1748
		if (empty($files)) {
1749
			if (!$args['upload'] && $args['name'] && is_array($args['name'])) {
1750
				$error = '';
1751
				$result['name'] = array();
1752
				foreach($args['name'] as $_i => $_name) {
1753
					if (!$volume->isUploadableByName($_name)) {
1754
						$error = $this->error(self::ERROR_UPLOAD_FILE, $_name, self::ERROR_UPLOAD_FILE_MIME);
1755
						break;
1756
					}
1757
					$result['name'][$_i] = preg_replace($ngReg, '_', $_name);
1758
				}
1759
				if ($error) {
1760
					$result['error'] = $error;
1761
					return $result;
1762
				}
1763
				$result = array_merge_recursive($result, $this->ls($args));
1764
				if (empty($result['list'])) {
1765
					$result['name'] = array();
1766
				} else {
1767
					$result['name'] = array_merge(array_intersect($result['name'], $result['list']));
1768
				}
1769
				return $result;
1770
			}
1771
			if (isset($args['upload']) && is_array($args['upload']) && ($tempDir = $this->getTempDir($volume->getTempPath()))) {
1772
				$names = array();
1773
				foreach($args['upload'] as $i => $url) {
1774
					// check chunked file upload commit
1775
					if ($args['chunk']) {
1776
						if ($url === 'chunkfail' && $args['mimes'] === 'chunkfail') {
1777
							$this->checkChunkedFile(null, $chunk, $cid, $tempDir);
1778
							if (preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m)) {
1779
								$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $m[1], self::ERROR_UPLOAD_TRANSFER);
1780
							}
1781
							return $result;
1782
						} else {
1783
							$tmpfname = $tempDir . '/' . $args['chunk'];
1784
							$files['tmp_name'][$i] = $tmpfname;
1785
							$files['name'][$i] = $url;
1786
							$files['error'][$i] = 0;
1787
							$GLOBALS['elFinderTempFiles'][$tmpfname] = true;
1788
							break;
1789
						}
1790
					}
1791
					
1792
					$tmpfname = $tempDir . DIRECTORY_SEPARATOR . 'ELF_FATCH_' . md5($url.microtime(true));
1793
					
1794
					$_name = '';
1795
					// check is data:
1796
					if (substr($url, 0, 5) === 'data:') {
1797
						list($data, $args['name'][$i]) = $this->parse_data_scheme($url, $extTable);
1798
					} else {
1799
						$fp = fopen($tmpfname, 'wb');
1800
						$data = $this->get_remote_contents($url, 30, 5, 'Mozilla/5.0', $fp);
1801
						$_POST['overwrite'] = false;
1802
						$_name = preg_replace('~^.*?([^/#?]+)(?:\?.*)?(?:#.*)?$~', '$1', rawurldecode($url));
1803
						// Check `Content-Disposition` response header
1804
						if ($data && ($headers = get_headers($url, true)) && !empty($headers['Content-Disposition'])) {
1805
							if (preg_match('/filename\*?=(?:(.+?)\'\')?"?([a-z0-9_.~%-]+)"?/i', $headers['Content-Disposition'], $m)) {
1806
								$_name = rawurldecode($m[2]);
1807
								if ($m[1] && strtoupper($m[1]) !== 'UTF-8' && function_exists('mb_convert_encoding')) {
1808
									$_name = mb_convert_encoding($_name, 'UTF-8', $m[1]);
1809
								}
1810
							}
1811
						}
1812
					}
1813
					if ($data) {
1814
						if (isset($args['name'][$i])) {
1815
							$_name = $args['name'][$i];
1816
						}
1817
						if ($_name) {
1818
							$_ext = '';
1819
							if (preg_match('/(\.[a-z0-9]{1,7})$/', $_name, $_match)) {
1820
								$_ext = $_match[1];
1821
							}
1822
							if ((is_resource($data) && fclose($data)) || file_put_contents($tmpfname, $data)) {
1823
								$GLOBALS['elFinderTempFiles'][$tmpfname] = true;
1824
								$_name = preg_replace($ngReg, '_', $_name);
1825
								list($_a, $_b) = array_pad(explode('.', $_name, 2), 2, '');
1826
								if ($_b === '') {
1827
									if ($_ext) {
1828
										rename($tmpfname, $tmpfname . $_ext);
1829
										$tmpfname = $tmpfname . $_ext;
1830
									}
1831
									$_b = $this->detectFileExtension($tmpfname);
1832
									$_name = $_a.$_b;
1833
								} else {
1834
									$_b = '.'.$_b;
1835
								}
1836
								if (isset($names[$_name])) {
1837
									$_name = $_a.'_'.$names[$_name]++.$_b;
1838
								} else {
1839
									$names[$_name] = 1;
1840
								}
1841
								$files['tmp_name'][$i] = $tmpfname;
1842
								$files['name'][$i] = $_name;
1843
								$files['error'][$i] = 0;
1844
							} else {
1845
								@ 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...
1846
							}
1847
						}
1848
					}
1849
				}
1850
			}
1851
			if (empty($files)) {
1852
				return array_merge(array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_UPLOAD_NO_FILES)), $header);
1853
			}
1854
		}
1855
		
1856
		foreach ($files['name'] as $i => $name) {
1857
			if (($error = $files['error'][$i]) > 0) {
1858
				$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);
1859
				$this->uploadDebug = 'Upload error code: '.$error;
1860
				break;
1861
			}
1862
			
1863
			$tmpname = $files['tmp_name'][$i];
1864
			$path = ($paths && !empty($paths[$i]))? $paths[$i] : '';
1865
			if ($name === 'blob') {
1866
				if ($chunk) {
1867
					if ($tempDir = $this->getTempDir($volume->getTempPath())) {
1868
						list($tmpname, $name) = $this->checkChunkedFile($tmpname, $chunk, $cid, $tempDir, $volume);
1869
						if ($tmpname) {
1870
							if ($name === false) {
1871
								preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m);
1872
								$result['error'] = $this->error(self::ERROR_UPLOAD_FILE, $m[1], $tmpname);
1873
								$result['_chunkfailure'] = true;
1874
								$this->uploadDebug = 'Upload error: ' . $tmpname;
1875
							} else if ($name) {
1876
								$result['_chunkmerged'] = basename($tmpname);
1877
								$result['_name'] = $name;
1878
							}
1879
						}
1880
					} else {
1881
						$result['error'] = $this->error(self::ERROR_UPLOAD_FILE, $chunk, self::ERROR_UPLOAD_TRANSFER);
1882
						$this->uploadDebug = 'Upload error: unable open tmp file';
1883
					}
1884
					return $result;
1885 View Code Duplication
				} else {
0 ignored issues
show
This code seems to be duplicated across your project.

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

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

Loading history...
1886
					// for form clipboard with Google Chrome
1887
					$type = $files['type'][$i];
1888
					$ext = isset($extTable[$type])? '.' . $extTable[$type] : '';
1889
					$name = substr(md5(basename($tmpname)), 0, 8) . $ext;
1890
				}
1891
			}
1892
			
1893
			// 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...
1894
			if (! empty($this->listeners['upload.presave'])) {
1895 View Code Duplication
				foreach($this->listeners['upload.presave'] as $handler) {
0 ignored issues
show
This code seems to be duplicated across your project.

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

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

Loading history...
1896
					call_user_func_array($handler, array(&$path, &$name, $tmpname, $this, $volume));
1897
				}
1898
			}
1899
			
1900
			if (($fp = fopen($tmpname, 'rb')) == false) {
1901
				$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, self::ERROR_UPLOAD_TRANSFER);
1902
				$this->uploadDebug = 'Upload error: unable open tmp file';
1903
				if (! is_uploaded_file($tmpname)) {
1904
					if (@ unlink($tmpname)) unset($GLOBALS['elFinderTempFiles'][$tmpfname]);
1905
					continue;
1906
				}
1907
				break;
1908
			}
1909
			$rnres = array();
1910
			if ($path) {
1911
				$_target = $volume->getUploadTaget($target, $path, $result);
1912
			} else {
1913
				$_target = $target;
1914
				// file rename for backup
1915
				if (isset($renames[$name])) {
1916
					$dir = $volume->realpath($_target);
1917
					$hash = $volume->getHash($dir, $name);
1918
					$rnres = $this->rename(array('target' => $hash, 'name' => $volume->uniqueName($dir, $name, $suffix, true, 0)));
1919
					if (!empty($rnres['error'])) {
1920
						$result['warning'] = $rnres['error'];
1921
						break;
1922
					}
1923
				}
1924
			}
1925
			if (! $_target || ($file = $volume->upload($fp, $_target, $name, $tmpname)) === false) {
1926
				$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $volume->error());
1927
				fclose($fp);
1928 View Code Duplication
				if (! is_uploaded_file($tmpname)) {
0 ignored issues
show
This code seems to be duplicated across your project.

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

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

Loading history...
1929
					if (@ unlink($tmpname)) unset($GLOBALS['elFinderTempFiles'][$tmpname]);;
1930
					continue;
1931
				}
1932
				break;
1933
			}
1934
			
1935
			is_resource($fp) && fclose($fp);
1936
			if (! is_uploaded_file($tmpname)){
1937
				clearstatcache();
1938 View Code Duplication
				if (!is_file($tmpname) || @ unlink($tmpname)) {
0 ignored issues
show
This code seems to be duplicated across your project.

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

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

Loading history...
1939
					unset($GLOBALS['elFinderTempFiles'][$tmpname]);
1940
				}
1941
			}
1942
			$result['added'][] = $file;
1943
			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...
1944
				$result = array_merge_recursive($result, $rnres);
1945
			}
1946
		}
1947
		if ($GLOBALS['elFinderTempFiles']) {
1948
			foreach(array_keys($GLOBALS['elFinderTempFiles']) as $_temp) {
1949
				@ 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...
1950
			}
1951
		}
1952
		$result['removed'] = $volume->removed();
1953
		
1954
		if (!empty($args['node'])) {
1955
			$result['callback'] = array(
1956
				'node' => $args['node'],
1957
				'bind' => 'upload'
1958
			);
1959
		}
1960
		return $result;
1961
	}
1962
		
1963
	/**
1964
	 * Copy/move files into new destination
1965
	 *
1966
	 * @param  array  command arguments
1967
	 * @return array
1968
	 * @author Dmitry (dio) Levashov
1969
	 **/
1970
	protected function paste($args) {
1971
		$dst     = $args['dst'];
1972
		$targets = is_array($args['targets']) ? $args['targets'] : array();
1973
		$cut     = !empty($args['cut']);
1974
		$error   = $cut ? self::ERROR_MOVE : self::ERROR_COPY;
1975
		$result  = array('added' => array(), 'removed' => array());
1976
		
1977
		if (($dstVolume = $this->volume($dst)) == false) {
1978
			return array('error' => $this->error($error, '#'.$targets[0], self::ERROR_TRGDIR_NOT_FOUND, '#'.$dst));
1979
		}
1980
		
1981
		$renames = array();
1982
		$suffix = '~';
1983
		if (!empty($args['renames'])) {
1984
			$renames = array_flip($args['renames']);
1985 View Code Duplication
			if (is_string($args['suffix']) && ! preg_match('/[\/\\?*:|"<>]/', $args['suffix'])) {
0 ignored issues
show
This code seems to be duplicated across your project.

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

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

Loading history...
1986
				$suffix = $args['suffix'];
1987
			}
1988
		}
1989
		
1990
		foreach ($targets as $target) {
1991 View Code Duplication
			if (($srcVolume = $this->volume($target)) == false) {
1992
				$result['warning'] = $this->error($error, '#'.$target, self::ERROR_FILE_NOT_FOUND);
1993
				break;
1994
			}
1995
			
1996
			$rnres = array();
1997
			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...
1998
				$file = $srcVolume->file($target);
1999
				if (isset($renames[$file['name']])) {
2000
					$dir = $dstVolume->realpath($dst);
2001
					$hash = $dstVolume->getHash($dir, $file['name']);
2002
					$rnres = $this->rename(array('target' => $hash, 'name' => $dstVolume->uniqueName($dir, $file['name'], $suffix, true, 0)));
2003
					if (!empty($rnres['error'])) {
2004
						$result['warning'] = $rnres['error'];
2005
						break;
2006
					}
2007
				}
2008
			}
2009
			
2010
			if (($file = $dstVolume->paste($srcVolume, $target, $dst, $cut)) == false) {
2011
				$result['warning'] = $this->error($dstVolume->error());
2012
				break;
2013
			}
2014
			
2015
			$result['added'][] = $file;
2016
			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...
2017
				$result = array_merge_recursive($result, $rnres);
2018
			}
2019
		}
2020
		return $result;
2021
	}
2022
	
2023
	/**
2024
	 * Return file content
2025
	 *
2026
	 * @param  array  $args  command arguments
2027
	 * @return array
2028
	 * @author Dmitry (dio) Levashov
2029
	 **/
2030
	protected function get($args) {
2031
		$target = $args['target'];
2032
		$volume = $this->volume($target);
2033
		
2034
		if (!$volume || ($file = $volume->file($target)) == false) {
2035
			return array('error' => $this->error(self::ERROR_OPEN, '#'.$target, self::ERROR_FILE_NOT_FOUND));
2036
		}
2037
		
2038 View Code Duplication
		if (($content = $volume->getContents($target)) === false) {
2039
			return array('error' => $this->error(self::ERROR_OPEN, $volume->path($target), $volume->error()));
2040
		}
2041
		
2042
		if ($args['conv'] && function_exists('mb_detect_encoding') && function_exists('mb_convert_encoding')) {
2043
			$mime = isset($file['mime'])? $file['mime'] : '';
2044
			if ($mime && strtolower(substr($mime, 0, 4)) === 'text') {
2045
				if ($enc = mb_detect_encoding ( $content , mb_detect_order(), true)) {
2046
					if (strtolower($enc) !== 'utf-8') {
2047
						$content = mb_convert_encoding($content, 'UTF-8', $enc);
2048
					}
2049
				}
2050
			}
2051
		}
2052
		
2053
		$json = json_encode($content);
2054
2055
		if ($json === false || strlen($json) < strlen($content)) {
2056
			if ($args['conv']) {
2057
				return array('error' => $this->error(self::ERROR_CONV_UTF8,self::ERROR_NOT_UTF8_CONTENT, $volume->path($target)));
2058
			} else {
2059
				return array('doconv' => true);
2060
			}
2061
		}
2062
		
2063
		return array('content' => $content);
2064
	}
2065
	
2066
	/**
2067
	 * Save content into text file
2068
	 *
2069
	 * @return array
2070
	 * @author Dmitry (dio) Levashov
2071
	 **/
2072
	protected function put($args) {
2073
		$target = $args['target'];
2074
		
2075 View Code Duplication
		if (($volume = $this->volume($target)) == false
2076
		|| ($file = $volume->file($target)) == false) {
2077
			return array('error' => $this->error(self::ERROR_SAVE, '#'.$target, self::ERROR_FILE_NOT_FOUND));
2078
		}
2079
		
2080 View Code Duplication
		if (($file = $volume->putContents($target, $args['content'])) == false) {
2081
			return array('error' => $this->error(self::ERROR_SAVE, $volume->path($target), $volume->error()));
2082
		}
2083
		
2084
		return array('changed' => array($file));
2085
	}
2086
2087
	/**
2088
	 * Extract files from archive
2089
	 *
2090
	 * @param  array  $args  command arguments
2091
	 * @return array
2092
	 * @author Dmitry (dio) Levashov, 
2093
	 * @author Alexey Sukhotin
2094
	 **/
2095
	protected function extract($args) {
2096
		$target = $args['target'];
2097
		$mimes  = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array();
2098
		$error  = array(self::ERROR_EXTRACT, '#'.$target);
2099
		$makedir = isset($args['makedir'])? (bool)$args['makedir'] : null;
2100
2101 View Code Duplication
		if (($volume = $this->volume($target)) == false
2102
		|| ($file = $volume->file($target)) == false) {
2103
			return array('error' => $this->error(self::ERROR_EXTRACT, '#'.$target, self::ERROR_FILE_NOT_FOUND));
2104
		}  
2105
2106
		return ($file = $volume->extract($target, $makedir))
2107
			? array('added' => isset($file['read'])? array($file) : $file)
2108
			: array('error' => $this->error(self::ERROR_EXTRACT, $volume->path($target), $volume->error()));
2109
	}
2110
	
2111
	/**
2112
	 * Create archive
2113
	 *
2114
	 * @param  array  $args  command arguments
2115
	 * @return array
2116
	 * @author Dmitry (dio) Levashov, 
2117
	 * @author Alexey Sukhotin
2118
	 **/
2119
	protected function archive($args) {
2120
		$type    = $args['type'];
2121
		$targets = isset($args['targets']) && is_array($args['targets']) ? $args['targets'] : array();
2122
		$name    = isset($args['name'])? $args['name'] : '';
2123
	
2124 View Code Duplication
		if (($volume = $this->volume($targets[0])) == false) {
2125
			return $this->error(self::ERROR_ARCHIVE, self::ERROR_TRGDIR_NOT_FOUND);
2126
		}
2127
	
2128
		return ($file = $volume->archive($targets, $args['type'], $name))
2129
			? array('added' => array($file))
2130
			: array('error' => $this->error(self::ERROR_ARCHIVE, $volume->error()));
2131
	}
2132
	
2133
	/**
2134
	 * Search files
2135
	 *
2136
	 * @param  array  $args  command arguments
2137
	 * @return array
2138
	 * @author Dmitry Levashov
2139
	 **/
2140
	protected function search($args) {
2141
		$q      = trim($args['q']);
2142
		$mimes  = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array();
2143
		$target = !empty($args['target'])? $args['target'] : null;
2144
		$result = array();
2145
		$errors = array();
2146
2147
		if ($target) {
2148 View Code Duplication
			if ($volume = $this->volume($target)) {
0 ignored issues
show
This code seems to be duplicated across your project.

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

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

Loading history...
2149
				$result = $volume->search($q, $mimes, $target);
2150
				$errors = array_merge($errors, $volume->error());
2151
			}
2152 View Code Duplication
		} else {
0 ignored issues
show
This code seems to be duplicated across your project.

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

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

Loading history...
2153
			foreach ($this->volumes as $volume) {
2154
				$result = array_merge($result, $volume->search($q, $mimes));
2155
				$errors = array_merge($errors, $volume->error());
2156
			}
2157
		}
2158
		
2159
		$result = array('files' => $result);
2160
		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...
2161
			$result['warning'] = $errors;
2162
		}
2163
		return $result;
2164
	}
2165
	
2166
	/**
2167
	 * Return file info (used by client "places" ui)
2168
	 *
2169
	 * @param  array  $args  command arguments
2170
	 * @return array
2171
	 * @author Dmitry Levashov
2172
	 **/
2173
	protected function info($args) {
2174
		$files = array();
2175
		$sleep = 0;
0 ignored issues
show
$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...
2176
		$compare = null;
2177
		// long polling mode
2178
		if ($args['compare'] && count($args['targets']) === 1) {
2179
			$compare = intval($args['compare']);
2180
			$hash = $args['targets'][0];
2181
			if ($volume = $this->volume($hash)) {
2182
				$standby = (int)$volume->getOption('plStandby');
2183
				$_compare = false;
2184
				if (($syncCheckFunc = $volume->getOption('syncCheckFunc')) && is_callable($syncCheckFunc)) {
2185
					$_compare = call_user_func_array($syncCheckFunc, array($volume->realpath($hash), $standby, $compare, $volume, $this));
2186
				}
2187
				if ($_compare !== false) {
2188
					$compare = $_compare;
2189
				} else {
2190
					$sleep = max(1, (int)$volume->getOption('tsPlSleep'));
2191
					$limit = max(1, $standby / $sleep) + 1;
2192
					$timelimit = ini_get('max_execution_time');
2193
					do {
2194
						$timelimit && @ set_time_limit($timelimit + $sleep);
2195
						$volume->clearstatcache();
2196
						if (($info = $volume->file($hash)) != false) {
2197
							if ($info['ts'] != $compare) {
2198
								$compare = $info['ts'];
2199
								break;
2200
							}
2201
						} else {
2202
							$compare = 0;
2203
							break;
2204
						}
2205
						if (--$limit) {
2206
							sleep($sleep);
2207
						}
2208
					} while($limit);
2209
				}
2210
			}
2211
		} else {
2212
			foreach ($args['targets'] as $hash) {
2213
				if (($volume = $this->volume($hash)) != false
2214
				&& ($info = $volume->file($hash)) != false) {
2215
					$files[] = $info;
2216
				}
2217
			}
2218
		}
2219
		
2220
		$result = array('files' => $files);
2221
		if (!is_null($compare)) {
2222
			$result['compare'] = strval($compare);
2223
		}
2224
		return $result;
2225
	}
2226
	
2227
	/**
2228
	 * Return image dimmensions
2229
	 *
2230
	 * @param  array  $args  command arguments
2231
	 * @return array
2232
	 * @author Dmitry (dio) Levashov
2233
	 **/
2234
	protected function dim($args) {
2235
		$target = $args['target'];
2236
		
2237
		if (($volume = $this->volume($target)) != false) {
2238
			$dim = $volume->dimensions($target);
2239
			return $dim ? array('dim' => $dim) : array();
2240
		}
2241
		return array();
2242
	}
2243
	
2244
	/**
2245
	 * Resize image
2246
	 *
2247
	 * @param  array  command arguments
2248
	 * @return array
2249
	 * @author Dmitry (dio) Levashov
2250
	 * @author Alexey Sukhotin
2251
	 **/
2252
	protected function resize($args) {
2253
		$target = $args['target'];
2254
		$width  = $args['width'];
2255
		$height = $args['height'];
2256
		$x      = (int)$args['x'];
2257
		$y      = (int)$args['y'];
2258
		$mode   = $args['mode'];
2259
		$bg     = null;
2260
		$degree = (int)$args['degree'];
2261
		$quality= (int)$args['quality'];
2262
		
2263 View Code Duplication
		if (($volume = $this->volume($target)) == false
2264
		|| ($file = $volume->file($target)) == false) {
2265
			return array('error' => $this->error(self::ERROR_RESIZE, '#'.$target, self::ERROR_FILE_NOT_FOUND));
2266
		}
2267
2268
		return ($file = $volume->resize($target, $width, $height, $x, $y, $mode, $bg, $degree, $quality))
2269
			? array('changed' => array($file))
2270
			: array('error' => $this->error(self::ERROR_RESIZE, $volume->path($target), $volume->error()));
2271
	}
2272
	
2273
	/**
2274
	* Return content URL
2275
	*
2276
	* @param  array  $args  command arguments
2277
	* @return array
2278
	* @author Naoki Sawada
2279
	**/
2280
	protected function url($args) {
2281
		$target = $args['target'];
2282
		$options = isset($args['options'])? $args['options'] : array();
2283
		if (($volume = $this->volume($target)) != false) {
2284
			$url = $volume->getContentUrl($target, $options);
2285
			return $url ? array('url' => $url) : array();
2286
		}
2287
		return array();
2288
	}
2289
2290
	/**
2291
	 * Output callback result with JavaScript that control elFinder
2292
	 * or HTTP redirect to callbackWindowURL
2293
	 * 
2294
	 * @param  array  command arguments
2295
	 * @author Naoki Sawada
2296
	 */
2297
	protected function callback($args) {
2298
		$checkReg = '/[^a-zA-Z0-9;._-]/';
2299
		$node = (isset($args['node']) && !preg_match($checkReg, $args['node']))? $args['node'] : '';
2300
		$json = (isset($args['json']) && @json_decode($args['json']))? $args['json'] : '{}';
2301
		$bind  = (isset($args['bind']) && !preg_match($checkReg, $args['bind']))? $args['bind'] : '';
2302
		$done = (!empty($args['done']));
2303
		
2304
		while( ob_get_level() ) {
2305
			if (! ob_end_clean()) {
2306
				break;
2307
			}
2308
		}
2309
		
2310
		if ($done || ! $this->callbackWindowURL) {
2311
			$script = '';
2312
			if ($node) {
2313
				$script .= '
2314
					var w = window.opener || window.parent || window;
2315
					try {
2316
						var elf = w.document.getElementById(\''.$node.'\').elfinder;
2317
						if (elf) {
2318
							var data = '.$json.';
2319
							if (data.error) {
2320
								elf.error(data.error);
2321
							} else {
2322
								data.warning && elf.error(data.warning);
2323
								data.removed && data.removed.length && elf.remove(data);
2324
								data.added   && data.added.length   && elf.add(data);
2325
								data.changed && data.changed.length && elf.change(data);';
2326
				if ($bind) {
2327
					$script .= '
2328
								elf.trigger(\''.$bind.'\', data);';
2329
				}
2330
				$script .= '
2331
								data.sync && elf.sync();
2332
							}
2333
						}
2334
					} catch(e) {
2335
						// for CORS
2336
						w.postMessage && w.postMessage(JSON.stringify({bind:\''.$bind.'\',data:'.$json.'}), \'*\');
2337
					}';
2338
			}
2339
			$script .= 'window.close();';
2340
			
2341
			$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>';
2342
			
2343
			header('Content-Type: text/html; charset=utf-8');
2344
			header('Content-Length: '.strlen($out));
2345
			header('Cache-Control: private');
2346
			header('Pragma: no-cache');
2347
			
2348
			echo $out;
2349
			
2350
		} else {
2351
			$url = $this->callbackWindowURL;
2352
			$url .= ((strpos($url, '?') === false)? '?' : '&')
2353
				 . '&node=' . rawurlencode($node)
2354
				 . (($json !== '{}')? ('&json=' . rawurlencode($json)) : '')
2355
				 . ($bind? ('&bind=' .  rawurlencode($bind)) : '')
2356
				 . '&done=1';
2357
			
2358
			header('Location: ' . $url);
2359
			
2360
		}
2361
		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...
2362
	}
2363
2364
	/***************************************************************************/
2365
	/*                                   utils                                 */
2366
	/***************************************************************************/
2367
	
2368
	/**
2369
	 * Return root - file's owner
2370
	 *
2371
	 * @param  string  file hash
2372
	 * @return elFinderStorageDriver
2373
	 * @author Dmitry (dio) Levashov
2374
	 **/
2375
	protected function volume($hash) {
2376
		foreach ($this->volumes as $id => $v) {
2377
			if (strpos(''.$hash, $id) === 0) {
2378
				return $this->volumes[$id];
2379
			} 
2380
		}
2381
		return false;
2382
	}
2383
	
2384
	/**
2385
	 * Return files info array 
2386
	 *
2387
	 * @param  array  $data  one file info or files info
2388
	 * @return array
2389
	 * @author Dmitry (dio) Levashov
2390
	 **/
2391
	protected function toArray($data) {
2392
		return isset($data['hash']) || !is_array($data) ? array($data) : $data;
2393
	}
2394
	
2395
	/**
2396
	 * Return fils hashes list
2397
	 *
2398
	 * @param  array  $files  files info
2399
	 * @return array
2400
	 * @author Dmitry (dio) Levashov
2401
	 **/
2402
	protected function hashes($files) {
2403
		$ret = array();
2404
		foreach ($files as $file) {
2405
			$ret[] = $file['hash'];
2406
		}
2407
		return $ret;
2408
	}
2409
	
2410
	/**
2411
	 * Remove from files list hidden files and files with required mime types
2412
	 *
2413
	 * @param  array  $files  files info
2414
	 * @return array
2415
	 * @author Dmitry (dio) Levashov
2416
	 **/
2417
	protected function filter($files) {
2418
		foreach ($files as $i => $file) {
2419
			if (!empty($file['hidden']) || !$this->default->mimeAccepted($file['mime'])) {
2420
				unset($files[$i]);
2421
			}
2422
		}
2423
		return array_merge($files, array());
2424
	}
2425
	
2426
	protected function utime() {
2427
		$time = explode(" ", microtime());
2428
		return (double)$time[1] + (double)$time[0];
2429
	}
2430
	
2431
	
2432
	/***************************************************************************/
2433
	/*                           static  utils                                 */
2434
	/***************************************************************************/
2435
	
2436
	/**
2437
	 * Return Is Animation Gif
2438
	 * 
2439
	 * @param  string $path server local path of target image
2440
	 * @return bool
2441
	 */
2442
	public static function isAnimationGif($path) {
2443
		list($width, $height, $type, $attr) = getimagesize($path);
0 ignored issues
show
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...
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...
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...
2444
		switch ($type) {
2445
			case IMAGETYPE_GIF:
2446
				break;
2447
			default:
2448
				return false;
2449
		}
2450
	
2451
		$imgcnt = 0;
2452
		$fp = fopen($path, 'rb');
2453
		@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...
2454
		$c = @fread($fp,1);
2455
		if (ord($c) != 0x39) {  // GIF89a
2456
			return false;
2457
		}
2458
	
2459
		while (!feof($fp)) {
2460
			do {
2461
				$c = fread($fp, 1);
2462
			} while(ord($c) != 0x21 && !feof($fp));
2463
	
2464
			if (feof($fp)) {
2465
				break;
2466
			}
2467
	
2468
			$c2 = fread($fp,2);
2469
			if (bin2hex($c2) == "f904") {
2470
				$imgcnt++;
2471
			}
2472
	
2473
			if (feof($fp)) {
2474
				break;
2475
			}
2476
		}
2477
	
2478
		if ($imgcnt > 1) {
2479
			return true;
2480
		} else {
2481
			return false;
2482
		}
2483
	}
2484
2485
	/**
2486
	 * Return Is seekable stream resource
2487
	 * 
2488
	 * @param resource $resource
2489
	 * @return bool
2490
	 */
2491
	public static function isSeekableStream($resource) {
2492
		$metadata = stream_get_meta_data($resource);
2493
		return $metadata['seekable'];
2494
	}
2495
2496
	/**
2497
	 * serialize and base64_encode of session data (If needed)
2498
	 * 
2499
	 * @param  mixed $var  target variable
2500
	 * @author Naoki Sawada
2501
	 */
2502
	public static function sessionDataEncode($var) {
2503
		if (self::$base64encodeSessionData) {
2504
			$var = base64_encode(serialize($var));
2505
		}
2506
		return $var;
2507
	}
2508
	
2509
	/**
2510
	 * base64_decode and unserialize of session data  (If needed)
2511
	 * 
2512
	 * @param  mixed $var      target variable
2513
	 * @param  bool  $checkIs  data type for check (array|string|object|int)
2514
	 * @author Naoki Sawada
2515
	 */
2516
	public static function sessionDataDecode(&$var, $checkIs = null) {
2517
		if (self::$base64encodeSessionData) {
2518
			$data = @unserialize(@base64_decode($var));
2519
		} else {
2520
			$data = $var;
2521
		}
2522
		$chk = true;
2523
		if ($checkIs) {
2524
			switch ($checkIs) {
2525
				case 'array':
2526
					$chk = is_array($data);
2527
					break;
2528
				case 'string':
2529
					$chk = is_string($data);
2530
					break;
2531
				case 'object':
2532
					$chk = is_object($data);
2533
					break;
2534
				case 'int':
2535
					$chk = is_int($data);
2536
					break;
2537
			}
2538
		}
2539
		if (!$chk) {
2540
			unset($var);
2541
			return false;
2542
		}
2543
		return $data;
2544
	}
2545
} // END class
2546