This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | elFinder::$netDrivers['ftp'] = 'FTP'; |
||
4 | |||
5 | /** |
||
6 | * Simple elFinder driver for FTP |
||
7 | * |
||
8 | * @author Dmitry (dio) Levashov |
||
9 | * @author Cem (discofever) |
||
10 | **/ |
||
11 | class elFinderVolumeFTP extends elFinderVolumeDriver { |
||
12 | |||
13 | /** |
||
14 | * Driver id |
||
15 | * Must be started from letter and contains [a-z0-9] |
||
16 | * Used as part of volume id |
||
17 | * |
||
18 | * @var string |
||
19 | **/ |
||
20 | protected $driverId = 'f'; |
||
21 | |||
22 | /** |
||
23 | * FTP Connection Instance |
||
24 | * |
||
25 | * @var ftp |
||
26 | **/ |
||
27 | protected $connect = null; |
||
28 | |||
29 | /** |
||
30 | * Directory for tmp files |
||
31 | * If not set driver will try to use tmbDir as tmpDir |
||
32 | * |
||
33 | * @var string |
||
34 | **/ |
||
35 | protected $tmpPath = ''; |
||
36 | |||
37 | /** |
||
38 | * Last FTP error message |
||
39 | * |
||
40 | * @var string |
||
41 | **/ |
||
42 | protected $ftpError = ''; |
||
43 | |||
44 | /** |
||
45 | * FTP server output list as ftp on linux |
||
46 | * |
||
47 | * @var bool |
||
48 | **/ |
||
49 | protected $ftpOsUnix; |
||
50 | |||
51 | /** |
||
52 | * Tmp folder path |
||
53 | * |
||
54 | * @var string |
||
55 | **/ |
||
56 | protected $tmp = ''; |
||
57 | |||
58 | /** |
||
59 | * Net mount key |
||
60 | * |
||
61 | * @var string |
||
62 | **/ |
||
63 | public $netMountKey = ''; |
||
64 | |||
65 | /** |
||
66 | * FTP command `MLST` support |
||
67 | * |
||
68 | * @var bool |
||
69 | */ |
||
70 | private $MLSTsupprt = false; |
||
71 | |||
72 | /** |
||
73 | * Calling cacheDir() target path with non-MLST |
||
74 | * |
||
75 | * @var string |
||
76 | */ |
||
77 | private $cacheDirTarget = ''; |
||
78 | |||
79 | /** |
||
80 | * Constructor |
||
81 | * Extend options with required fields |
||
82 | * |
||
83 | * @return void |
||
84 | * @author Dmitry (dio) Levashov |
||
85 | * @author Cem (DiscoFever) |
||
86 | **/ |
||
87 | public function __construct() { |
||
88 | $opts = array( |
||
89 | 'host' => 'localhost', |
||
90 | 'user' => '', |
||
91 | 'pass' => '', |
||
92 | 'port' => 21, |
||
93 | 'mode' => 'passive', |
||
94 | 'path' => '/', |
||
95 | 'timeout' => 20, |
||
96 | 'owner' => true, |
||
97 | 'tmbPath' => '', |
||
98 | 'tmpPath' => '', |
||
99 | 'dirMode' => 0755, |
||
100 | 'fileMode' => 0644, |
||
101 | 'rootCssClass' => 'elfinder-navbar-root-ftp' |
||
102 | |||
103 | ); |
||
104 | $this->options = array_merge($this->options, $opts); |
||
105 | $this->options['mimeDetect'] = 'internal'; |
||
106 | $this->options['maxArcFilesSize'] = 0; // max allowed archive files size (0 - no limit) |
||
107 | } |
||
108 | |||
109 | /** |
||
110 | * Prepare |
||
111 | * Call from elFinder::netmout() before volume->mount() |
||
112 | * |
||
113 | * @return Array |
||
114 | * @author Naoki Sawada |
||
115 | **/ |
||
116 | public function netmountPrepare($options) { |
||
117 | if (!empty($_REQUEST['encoding']) && @iconv('UTF-8', $_REQUEST['encoding'], '') !== false) { |
||
118 | $options['encoding'] = $_REQUEST['encoding']; |
||
119 | if (!empty($_REQUEST['locale']) && @setlocale(LC_ALL, $_REQUEST['locale'])) { |
||
120 | setlocale(LC_ALL, elFinder::$locale); |
||
121 | $options['locale'] = $_REQUEST['locale']; |
||
122 | } |
||
123 | } |
||
124 | $options['statOwner'] = true; |
||
125 | $options['allowChmodReadOnly'] = true; |
||
126 | return $options; |
||
127 | } |
||
128 | |||
129 | /*********************************************************************/ |
||
130 | /* INIT AND CONFIGURE */ |
||
131 | /*********************************************************************/ |
||
132 | |||
133 | /** |
||
134 | * Prepare FTP connection |
||
135 | * Connect to remote server and check if credentials are correct, if so, store the connection id in $ftp_conn |
||
136 | * |
||
137 | * @return bool |
||
138 | * @author Dmitry (dio) Levashov |
||
139 | * @author Cem (DiscoFever) |
||
140 | **/ |
||
141 | protected function init() { |
||
142 | if (!$this->options['host'] |
||
143 | || !$this->options['port']) { |
||
144 | return $this->setError('Required options undefined.'); |
||
145 | } |
||
146 | |||
147 | if (!$this->options['user']) { |
||
148 | $this->options['user'] = 'anonymous'; |
||
149 | $this->options['pass'] = ''; |
||
150 | } |
||
151 | if (!$this->options['path']) { |
||
152 | $this->options['path'] = '/'; |
||
153 | } |
||
154 | |||
155 | // make ney mount key |
||
156 | $this->netMountKey = md5(join('-', array('ftp', $this->options['host'], $this->options['port'], $this->options['path'], $this->options['user']))); |
||
157 | |||
158 | if (!function_exists('ftp_connect')) { |
||
159 | return $this->setError('FTP extension not loaded.'); |
||
160 | } |
||
161 | |||
162 | // remove protocol from host |
||
163 | $scheme = parse_url($this->options['host'], PHP_URL_SCHEME); |
||
164 | |||
165 | if ($scheme) { |
||
0 ignored issues
–
show
|
|||
166 | $this->options['host'] = substr($this->options['host'], strlen($scheme)+3); |
||
167 | } |
||
168 | |||
169 | // normalize root path |
||
170 | $this->root = $this->options['path'] = $this->_normpath($this->options['path']); |
||
171 | |||
172 | View Code Duplication | if (empty($this->options['alias'])) { |
|
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. ![]() |
|||
173 | $this->options['alias'] = $this->options['user'].'@'.$this->options['host']; |
||
174 | // $num = elFinder::$volumesCnt-1; |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
46% 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. ![]() |
|||
175 | // $this->options['alias'] = $this->root == '/' || $this->root == '.' ? 'FTP folder '.$num : basename($this->root); |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
49% 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. ![]() |
|||
176 | } |
||
177 | |||
178 | $this->rootName = $this->options['alias']; |
||
179 | $this->options['separator'] = '/'; |
||
180 | |||
181 | if (is_null($this->options['syncChkAsTs'])) { |
||
182 | $this->options['syncChkAsTs'] = true; |
||
183 | } |
||
184 | |||
185 | return $this->connect(); |
||
186 | |||
187 | } |
||
188 | |||
189 | |||
190 | /** |
||
191 | * Configure after successfull mount. |
||
192 | * |
||
193 | * @return void |
||
194 | * @author Dmitry (dio) Levashov |
||
195 | **/ |
||
196 | protected function configure() { |
||
197 | parent::configure(); |
||
198 | |||
199 | View Code Duplication | if (!empty($this->options['tmpPath'])) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
200 | if ((is_dir($this->options['tmpPath']) || @mkdir($this->options['tmpPath'], 0755, true)) && is_writable($this->options['tmpPath'])) { |
||
201 | $this->tmp = $this->options['tmpPath']; |
||
202 | } |
||
203 | } |
||
204 | |||
205 | if (!$this->tmp && $this->tmbPath) { |
||
206 | $this->tmp = $this->tmbPath; |
||
207 | } |
||
208 | |||
209 | if (!$this->tmp) { |
||
210 | $this->disabled[] = 'mkfile'; |
||
211 | $this->disabled[] = 'paste'; |
||
212 | $this->disabled[] = 'duplicate'; |
||
213 | $this->disabled[] = 'upload'; |
||
214 | $this->disabled[] = 'edit'; |
||
215 | $this->disabled[] = 'archive'; |
||
216 | $this->disabled[] = 'extract'; |
||
217 | } |
||
218 | |||
219 | // echo $this->tmp; |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
58% 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. ![]() |
|||
220 | |||
221 | } |
||
222 | |||
223 | /** |
||
224 | * Connect to ftp server |
||
225 | * |
||
226 | * @return bool |
||
227 | * @author Dmitry (dio) Levashov |
||
228 | **/ |
||
229 | protected function connect() { |
||
230 | View Code Duplication | if (!($this->connect = ftp_connect($this->options['host'], $this->options['port'], $this->options['timeout']))) { |
|
0 ignored issues
–
show
It seems like
ftp_connect($this->optio...is->options['timeout']) of type resource is incompatible with the declared type object<ftp> of property $connect .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. ![]() 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. ![]() |
|||
231 | return $this->setError('Unable to connect to FTP server '.$this->options['host']); |
||
232 | } |
||
233 | View Code Duplication | if (!ftp_login($this->connect, $this->options['user'], $this->options['pass'])) { |
|
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. ![]() |
|||
234 | $this->umount(); |
||
235 | return $this->setError('Unable to login into '.$this->options['host']); |
||
236 | } |
||
237 | |||
238 | // try switch utf8 mode |
||
239 | if ($this->encoding) { |
||
240 | @ftp_exec($this->connect, 'OPTS UTF8 OFF'); |
||
241 | } else { |
||
242 | @ftp_exec($this->connect, 'OPTS UTF8 ON' ); |
||
243 | } |
||
244 | |||
245 | // switch off extended passive mode - may be usefull for some servers |
||
246 | @ftp_exec($this->connect, 'epsv4 off' ); |
||
0 ignored issues
–
show
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.');
}
![]() |
|||
247 | // enter passive mode if required |
||
248 | ftp_pasv($this->connect, $this->options['mode'] == 'passive'); |
||
249 | |||
250 | // enter root folder |
||
251 | if (!ftp_chdir($this->connect, $this->root) |
||
252 | || $this->root != ftp_pwd($this->connect)) { |
||
253 | $this->umount(); |
||
254 | return $this->setError('Unable to open root folder.'); |
||
255 | } |
||
256 | |||
257 | // check for MLST support |
||
258 | $features = ftp_raw($this->connect, 'FEAT'); |
||
259 | if (!is_array($features)) { |
||
260 | $this->umount(); |
||
261 | return $this->setError('Server does not support command FEAT.'); |
||
262 | } |
||
263 | |||
264 | foreach ($features as $feat) { |
||
265 | if (strpos(trim($feat), 'MLST') === 0) { |
||
266 | $this->MLSTsupprt = true; |
||
267 | break; |
||
268 | } |
||
269 | } |
||
270 | |||
271 | return true; |
||
272 | } |
||
273 | |||
274 | /*********************************************************************/ |
||
275 | /* FS API */ |
||
276 | /*********************************************************************/ |
||
277 | |||
278 | /** |
||
279 | * Close opened connection |
||
280 | * |
||
281 | * @return void |
||
282 | * @author Dmitry (dio) Levashov |
||
283 | **/ |
||
284 | public function umount() { |
||
285 | $this->connect && @ftp_close($this->connect); |
||
286 | } |
||
287 | |||
288 | |||
289 | /** |
||
290 | * Parse line from ftp_rawlist() output and return file stat (array) |
||
291 | * |
||
292 | * @param string $raw line from ftp_rawlist() output |
||
293 | * @return array |
||
294 | * @author Dmitry Levashov |
||
295 | **/ |
||
296 | protected function parseRaw($raw, $base, $nameOnly = false) { |
||
297 | $info = preg_split("/\s+/", $raw, 9); |
||
298 | $stat = array(); |
||
299 | |||
300 | View Code Duplication | if (!isset($this->ftpOsUnix)) { |
|
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. ![]() |
|||
301 | $this->ftpOsUnix = !preg_match('/\d/', substr($info[0], 0, 1)); |
||
302 | } |
||
303 | if (!$this->ftpOsUnix) { |
||
304 | $info = $this->normalizeRawWindows($raw); |
||
305 | } |
||
306 | |||
307 | if (count($info) < 9 || $info[8] == '.' || $info[8] == '..') { |
||
308 | return false; |
||
309 | } |
||
310 | |||
311 | $name = $info[8]; |
||
312 | |||
313 | if (preg_match('|(.+)\-\>(.+)|', $name, $m)) { |
||
314 | $name = trim($m[1]); |
||
315 | // check recursive processing |
||
316 | if ($this->cacheDirTarget && $this->_joinPath($base, $name) !== $this->cacheDirTarget) { |
||
317 | return array(); |
||
318 | } |
||
319 | if (!$nameOnly) { |
||
320 | $target = trim($m[2]); |
||
321 | if (substr($target, 0, 1) !== $this->separator) { |
||
322 | $target = $this->getFullPath($target, $base); |
||
323 | } |
||
324 | $target = $this->_normpath($target); |
||
325 | $stat['name'] = $name; |
||
326 | $stat['target'] = $target; |
||
327 | return $stat; |
||
328 | } |
||
329 | } |
||
330 | |||
331 | if ($nameOnly) { |
||
332 | return array('name' => $name); |
||
333 | } |
||
334 | |||
335 | if (is_numeric($info[5]) && !$info[6] && !$info[7]) { |
||
336 | // by normalizeRawWindows() |
||
337 | $stat['ts'] = $info[5]; |
||
338 | } else { |
||
339 | $stat['ts'] = strtotime($info[5].' '.$info[6].' '.$info[7]); |
||
340 | if (empty($stat['ts'])) { |
||
341 | $stat['ts'] = strtotime($info[6].' '.$info[5].' '.$info[7]); |
||
342 | } |
||
343 | } |
||
344 | |||
345 | $stat['owner'] = ''; |
||
346 | if ($this->options['statOwner']) { |
||
347 | $stat['owner'] = $info[2]; |
||
348 | $stat['group'] = $info[3]; |
||
349 | $stat['perm'] = substr($info[0], 1); |
||
350 | $stat['isowner'] = $stat['owner']? ($stat['owner'] == $this->options['user']) : $this->options['owner']; |
||
351 | } |
||
352 | |||
353 | $perm = $this->parsePermissions($info[0], $stat['owner']); |
||
354 | $stat['name'] = $name; |
||
355 | $stat['mime'] = substr(strtolower($info[0]), 0, 1) == 'd' ? 'directory' : $this->mimetype($stat['name']); |
||
356 | $stat['size'] = $stat['mime'] == 'directory' ? 0 : $info[4]; |
||
357 | $stat['read'] = $perm['read']; |
||
358 | $stat['write'] = $perm['write']; |
||
359 | |||
360 | return $stat; |
||
361 | } |
||
362 | |||
363 | /** |
||
364 | * Normalize MS-DOS style FTP LIST Raw line |
||
365 | * |
||
366 | * @param string $raw line from FTP LIST (MS-DOS style) |
||
367 | * @return array |
||
368 | * @author Naoki Sawada |
||
369 | **/ |
||
370 | protected function normalizeRawWindows($raw) { |
||
371 | $info = array_pad(array(), 9, ''); |
||
372 | $item = preg_replace('#\s+#', ' ', trim($raw), 3); |
||
373 | list($date, $time, $size, $name) = explode(' ', $item, 4); |
||
374 | $format = strlen($date) === 8 ? 'm-d-yH:iA' : 'Y-m-dH:i'; |
||
375 | $dateObj = DateTime::createFromFormat($format, $date.$time); |
||
376 | $info[5] = strtotime($dateObj->format('Y-m-d H:i')); |
||
377 | $info[8] = $name; |
||
378 | if ($size === '<DIR>') { |
||
379 | $info[4] = 0; |
||
380 | $info[0] = 'drwxr-xr-x'; |
||
381 | } else { |
||
382 | $info[4] = (int)$size; |
||
383 | $info[0] = '-rw-r--r--'; |
||
384 | } |
||
385 | return $info; |
||
386 | } |
||
387 | |||
388 | /** |
||
389 | * Parse permissions string. Return array(read => true/false, write => true/false) |
||
390 | * |
||
391 | * @param string $perm permissions string |
||
392 | * @return string |
||
393 | * @author Dmitry (dio) Levashov |
||
394 | **/ |
||
395 | protected function parsePermissions($perm, $user = '') { |
||
396 | $res = array(); |
||
0 ignored issues
–
show
$res is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the ![]() |
|||
397 | $parts = array(); |
||
398 | $owner = $user? ($user == $this->options['user']) : $this->options['owner']; |
||
399 | for ($i = 0, $l = strlen($perm); $i < $l; $i++) { |
||
400 | $parts[] = substr($perm, $i, 1); |
||
401 | } |
||
402 | |||
403 | $read = ($owner && $parts[1] == 'r') || $parts[4] == 'r' || $parts[7] == 'r'; |
||
404 | |||
405 | return array( |
||
406 | 'read' => $parts[0] == 'd' ? $read && (($owner && $parts[3] == 'x') || $parts[6] == 'x' || $parts[9] == 'x') : $read, |
||
407 | 'write' => ($owner && $parts[2] == 'w') || $parts[5] == 'w' || $parts[8] == 'w' |
||
408 | ); |
||
409 | } |
||
410 | |||
411 | /** |
||
412 | * Cache dir contents |
||
413 | * |
||
414 | * @param string $path dir path |
||
415 | * @return void |
||
416 | * @author Dmitry Levashov |
||
417 | **/ |
||
418 | protected function cacheDir($path) { |
||
419 | $this->dirsCache[$path] = array(); |
||
420 | |||
421 | $list = array(); |
||
422 | $encPath = $this->convEncIn($path); |
||
423 | foreach (ftp_rawlist($this->connect, $encPath) as $raw) { |
||
424 | if (($stat = $this->parseRaw($raw, $encPath))) { |
||
425 | $list[] = $stat; |
||
426 | } |
||
427 | } |
||
428 | $list = $this->convEncOut($list); |
||
429 | $prefix = ($path === $this->separator)? $this->separator : $path . $this->separator; |
||
430 | $targets = array(); |
||
431 | foreach($list as $stat) { |
||
432 | $p = $prefix . $stat['name']; |
||
433 | View Code Duplication | if (isset($stat['target'])) { |
|
434 | // stat later |
||
435 | $targets[$stat['name']] = $stat['target']; |
||
436 | } else { |
||
437 | $stat = $this->updateCache($p, $stat); |
||
438 | if (empty($stat['hidden'])) { |
||
439 | $this->dirsCache[$path][] = $p; |
||
440 | } |
||
441 | } |
||
442 | } |
||
443 | // stat link targets |
||
444 | foreach($targets as $name => $target) { |
||
445 | $stat = array(); |
||
446 | $stat['name'] = $name; |
||
447 | $p = $prefix . $name; |
||
448 | $cacheDirTarget = $this->cacheDirTarget; |
||
449 | $this->cacheDirTarget = $this->convEncIn($target, true); |
||
450 | if ($tstat = $this->stat($target)) { |
||
451 | $stat['size'] = $tstat['size']; |
||
452 | $stat['alias'] = $target; |
||
453 | $stat['thash'] = $tstat['hash']; |
||
454 | $stat['mime'] = $tstat['mime']; |
||
455 | $stat['read'] = $tstat['read']; |
||
456 | $stat['write'] = $tstat['write']; |
||
457 | |||
458 | if (isset($tstat['ts'])) { $stat['ts'] = $tstat['ts']; } |
||
459 | if (isset($tstat['owner'])) { $stat['owner'] = $tstat['owner']; } |
||
460 | if (isset($tstat['group'])) { $stat['group'] = $tstat['group']; } |
||
461 | if (isset($tstat['perm'])) { $stat['perm'] = $tstat['perm']; } |
||
462 | if (isset($tstat['isowner'])) { $stat['isowner'] = $tstat['isowner']; } |
||
463 | } else { |
||
464 | |||
465 | $stat['mime'] = 'symlink-broken'; |
||
466 | $stat['read'] = false; |
||
467 | $stat['write'] = false; |
||
468 | $stat['size'] = 0; |
||
469 | |||
470 | } |
||
471 | $this->cacheDirTarget = $cacheDirTarget; |
||
472 | $stat = $this->updateCache($p, $stat); |
||
473 | if (empty($stat['hidden'])) { |
||
474 | $this->dirsCache[$path][] = $p; |
||
475 | } |
||
476 | } |
||
477 | |||
478 | } |
||
479 | |||
480 | /** |
||
481 | * Return ftp transfer mode for file |
||
482 | * |
||
483 | * @param string $path file path |
||
484 | * @return string |
||
485 | * @author Dmitry (dio) Levashov |
||
486 | **/ |
||
487 | protected function ftpMode($path) { |
||
488 | return strpos($this->mimetype($path), 'text/') === 0 ? FTP_ASCII : FTP_BINARY; |
||
489 | } |
||
490 | |||
491 | /*********************** paths/urls *************************/ |
||
492 | |||
493 | /** |
||
494 | * Return parent directory path |
||
495 | * |
||
496 | * @param string $path file path |
||
497 | * @return string |
||
498 | * @author Dmitry (dio) Levashov |
||
499 | **/ |
||
500 | protected function _dirname($path) { |
||
501 | return dirname($path); |
||
502 | } |
||
503 | |||
504 | /** |
||
505 | * Return file name |
||
506 | * |
||
507 | * @param string $path file path |
||
508 | * @return string |
||
509 | * @author Dmitry (dio) Levashov |
||
510 | **/ |
||
511 | protected function _basename($path) { |
||
512 | return basename($path); |
||
513 | } |
||
514 | |||
515 | /** |
||
516 | * Join dir name and file name and retur full path |
||
517 | * |
||
518 | * @param string $dir |
||
519 | * @param string $name |
||
520 | * @return string |
||
521 | * @author Dmitry (dio) Levashov |
||
522 | **/ |
||
523 | protected function _joinPath($dir, $name) { |
||
524 | return rtrim($dir, $this->separator).$this->separator.$name; |
||
525 | } |
||
526 | |||
527 | /** |
||
528 | * Return normalized path, this works the same as os.path.normpath() in Python |
||
529 | * |
||
530 | * @param string $path path |
||
531 | * @return string |
||
532 | * @author Troex Nevelin |
||
533 | **/ |
||
534 | protected function _normpath($path) { |
||
535 | if (empty($path)) { |
||
536 | $path = '.'; |
||
537 | } |
||
538 | // path must be start with / |
||
539 | $path = preg_replace('|^\.\/?|', $this->separator, $path); |
||
540 | $path = preg_replace('/^([^\/])/', "/$1", $path); |
||
541 | |||
542 | if ($path[0] === $this->separator) { |
||
543 | $initial_slashes = true; |
||
544 | } else { |
||
545 | $initial_slashes = false; |
||
546 | } |
||
547 | |||
548 | View Code Duplication | if (($initial_slashes) |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
549 | && (strpos($path, '//') === 0) |
||
550 | && (strpos($path, '///') === false)) { |
||
551 | $initial_slashes = 2; |
||
552 | } |
||
553 | |||
554 | $initial_slashes = (int) $initial_slashes; |
||
555 | |||
556 | $comps = explode($this->separator, $path); |
||
557 | $new_comps = array(); |
||
558 | View Code Duplication | foreach ($comps as $comp) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
559 | if (in_array($comp, array('', '.'))) { |
||
560 | continue; |
||
561 | } |
||
562 | |||
563 | if (($comp != '..') |
||
564 | || (!$initial_slashes && !$new_comps) |
||
0 ignored issues
–
show
The expression
$new_comps of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||
565 | || ($new_comps && (end($new_comps) == '..'))) { |
||
0 ignored issues
–
show
The expression
$new_comps of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||
566 | array_push($new_comps, $comp); |
||
567 | } elseif ($new_comps) { |
||
0 ignored issues
–
show
The expression
$new_comps of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||
568 | array_pop($new_comps); |
||
569 | } |
||
570 | } |
||
571 | $comps = $new_comps; |
||
572 | $path = implode($this->separator, $comps); |
||
573 | if ($initial_slashes) { |
||
574 | $path = str_repeat($this->separator, $initial_slashes) . $path; |
||
575 | } |
||
576 | |||
577 | return $path ? $path : '.'; |
||
578 | } |
||
579 | |||
580 | /** |
||
581 | * Return file path related to root dir |
||
582 | * |
||
583 | * @param string $path file path |
||
584 | * @return string |
||
585 | * @author Dmitry (dio) Levashov |
||
586 | **/ |
||
587 | View Code Duplication | protected function _relpath($path) { |
|
588 | if ($path === $this->root) { |
||
589 | return ''; |
||
590 | } else { |
||
591 | if (strpos($path, $this->root) === 0) { |
||
592 | return ltrim(substr($path, strlen($this->root)), $this->separator); |
||
593 | } else { |
||
594 | // for link |
||
595 | return $path; |
||
596 | } |
||
597 | } |
||
598 | } |
||
599 | |||
600 | /** |
||
601 | * Convert path related to root dir into real path |
||
602 | * |
||
603 | * @param string $path file path |
||
604 | * @return string |
||
605 | * @author Dmitry (dio) Levashov |
||
606 | **/ |
||
607 | protected function _abspath($path) { |
||
608 | if ($path === $this->separator) { |
||
609 | return $this->root; |
||
610 | } else { |
||
611 | if ($path[0] === $this->separator) { |
||
612 | // for link |
||
613 | return $path; |
||
614 | } else { |
||
615 | return $this->_joinPath($this->root, $path); |
||
616 | } |
||
617 | } |
||
618 | } |
||
619 | |||
620 | /** |
||
621 | * Return fake path started from root dir |
||
622 | * |
||
623 | * @param string $path file path |
||
624 | * @return string |
||
625 | * @author Dmitry (dio) Levashov |
||
626 | **/ |
||
627 | protected function _path($path) { |
||
628 | return $this->rootName.($path == $this->root ? '' : $this->separator.$this->_relpath($path)); |
||
629 | } |
||
630 | |||
631 | /** |
||
632 | * Return true if $path is children of $parent |
||
633 | * |
||
634 | * @param string $path path to check |
||
635 | * @param string $parent parent path |
||
636 | * @return bool |
||
637 | * @author Dmitry (dio) Levashov |
||
638 | **/ |
||
639 | protected function _inpath($path, $parent) { |
||
640 | return $path == $parent || strpos($path, $parent. $this->separator) === 0; |
||
641 | } |
||
642 | |||
643 | /***************** file stat ********************/ |
||
644 | /** |
||
645 | * Return stat for given path. |
||
646 | * Stat contains following fields: |
||
647 | * - (int) size file size in b. required |
||
648 | * - (int) ts file modification time in unix time. required |
||
649 | * - (string) mime mimetype. required for folders, others - optionally |
||
650 | * - (bool) read read permissions. required |
||
651 | * - (bool) write write permissions. required |
||
652 | * - (bool) locked is object locked. optionally |
||
653 | * - (bool) hidden is object hidden. optionally |
||
654 | * - (string) alias for symlinks - link target path relative to root path. optionally |
||
655 | * - (string) target for symlinks - link target path. optionally |
||
656 | * |
||
657 | * If file does not exists - returns empty array or false. |
||
658 | * |
||
659 | * @param string $path file path |
||
660 | * @return array|false |
||
661 | * @author Dmitry (dio) Levashov |
||
662 | **/ |
||
663 | protected function _stat($path) { |
||
664 | $outPath = $this->convEncOut($path); |
||
665 | if (isset($this->cache[$outPath])) { |
||
666 | return $this->convEncIn($this->cache[$outPath]); |
||
667 | } else { |
||
668 | $this->convEncIn(); |
||
669 | } |
||
670 | if (!$this->MLSTsupprt) { |
||
671 | //if ($path == $this->root && (empty($this->ARGS['reload']) || !isset($this->ARGS['target']) || strpos($this->ARGS['target'], $this->id) !== 0)) { |
||
672 | if ($path == $this->root && ! $this->isMyReload()) { |
||
673 | return array( |
||
674 | 'name' => $this->root, |
||
675 | 'mime' => 'directory', |
||
676 | 'dirs' => $this->_subdirs($path) |
||
677 | ); |
||
678 | } |
||
679 | $this->cacheDir($this->convEncOut($this->_dirname($path))); |
||
680 | return $this->convEncIn(isset($this->cache[$outPath])? $this->cache[$outPath] : array()); |
||
681 | } |
||
682 | $raw = ftp_raw($this->connect, 'MLST ' . $path); |
||
683 | if (is_array($raw) && count($raw) > 1 && substr(trim($raw[0]), 0, 1) == 2) { |
||
684 | $parts = explode(';', trim($raw[1])); |
||
685 | array_pop($parts); |
||
686 | $parts = array_map('strtolower', $parts); |
||
687 | $stat = array(); |
||
688 | foreach ($parts as $part) { |
||
689 | |||
690 | list($key, $val) = explode('=', $part); |
||
691 | |||
692 | switch ($key) { |
||
693 | case 'type': |
||
694 | $stat['mime'] = strpos($val, 'dir') !== false ? 'directory' : $this->mimetype($path); |
||
695 | break; |
||
696 | |||
697 | case 'size': |
||
698 | $stat['size'] = $val; |
||
699 | break; |
||
700 | |||
701 | case 'modify': |
||
702 | $ts = mktime(intval(substr($val, 8, 2)), intval(substr($val, 10, 2)), intval(substr($val, 12, 2)), intval(substr($val, 4, 2)), intval(substr($val, 6, 2)), substr($val, 0, 4)); |
||
703 | $stat['ts'] = $ts; |
||
704 | break; |
||
705 | |||
706 | case 'unix.mode': |
||
707 | $stat['chmod'] = $val; |
||
708 | break; |
||
709 | |||
710 | case 'perm': |
||
711 | $val = strtolower($val); |
||
712 | $stat['read'] = (int)preg_match('/e|l|r/', $val); |
||
713 | $stat['write'] = (int)preg_match('/w|m|c/', $val); |
||
714 | if (!preg_match('/f|d/', $val)) { |
||
715 | $stat['locked'] = 1; |
||
716 | } |
||
717 | break; |
||
718 | } |
||
719 | } |
||
720 | if (empty($stat['mime'])) { |
||
721 | return array(); |
||
722 | } |
||
723 | if ($stat['mime'] == 'directory') { |
||
724 | $stat['size'] = 0; |
||
725 | } |
||
726 | |||
727 | if (isset($stat['chmod'])) { |
||
728 | $stat['perm'] = ''; |
||
729 | if ($stat['chmod'][0] == 0) { |
||
730 | $stat['chmod'] = substr($stat['chmod'], 1); |
||
731 | } |
||
732 | |||
733 | for ($i = 0; $i <= 2; $i++) { |
||
734 | $perm[$i] = array(false, false, false); |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
$perm was never initialized. Although not strictly required by PHP, it is generally a good practice to add $perm = array(); before regardless.
Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code. Let’s take a look at an example: foreach ($collection as $item) {
$myArray['foo'] = $item->getFoo();
if ($item->hasBar()) {
$myArray['bar'] = $item->getBar();
}
// do something with $myArray
}
As you can see in this example, the array This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop. ![]() |
|||
735 | $n = isset($stat['chmod'][$i]) ? $stat['chmod'][$i] : 0; |
||
736 | |||
737 | View Code Duplication | if ($n - 4 >= 0) { |
|
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. ![]() |
|||
738 | $perm[$i][0] = true; |
||
0 ignored issues
–
show
The variable
$perm does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
![]() |
|||
739 | $n = $n - 4; |
||
740 | $stat['perm'] .= 'r'; |
||
741 | } else { |
||
742 | $stat['perm'] .= '-'; |
||
743 | } |
||
744 | |||
745 | View Code Duplication | if ($n - 2 >= 0) { |
|
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. ![]() |
|||
746 | $perm[$i][1] = true; |
||
747 | $n = $n - 2; |
||
748 | $stat['perm'] .= 'w'; |
||
749 | } else { |
||
750 | $stat['perm'] .= '-'; |
||
751 | } |
||
752 | |||
753 | View Code Duplication | if ($n - 1 == 0) { |
|
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. ![]() |
|||
754 | $perm[$i][2] = true; |
||
755 | $stat['perm'] .= 'x'; |
||
756 | } else { |
||
757 | $stat['perm'] .= '-'; |
||
758 | } |
||
759 | |||
760 | $stat['perm'] .= ' '; |
||
761 | } |
||
762 | |||
763 | $stat['perm'] = trim($stat['perm']); |
||
764 | |||
765 | $owner = $this->options['owner']; |
||
766 | $read = ($owner && $perm[0][0]) || $perm[1][0] || $perm[2][0]; |
||
767 | |||
768 | $stat['read'] = $stat['mime'] == 'directory' ? $read && (($owner && $perm[0][2]) || $perm[1][2] || $perm[2][2]) : $read; |
||
769 | $stat['write'] = ($owner && $perm[0][1]) || $perm[1][1] || $perm[2][1]; |
||
770 | unset($stat['chmod']); |
||
771 | |||
772 | } |
||
773 | |||
774 | return $stat; |
||
775 | |||
776 | } |
||
777 | |||
778 | return array(); |
||
779 | } |
||
780 | |||
781 | /** |
||
782 | * Return true if path is dir and has at least one childs directory |
||
783 | * |
||
784 | * @param string $path dir path |
||
785 | * @return bool |
||
786 | * @author Dmitry (dio) Levashov |
||
787 | **/ |
||
788 | protected function _subdirs($path) { |
||
789 | |||
790 | foreach (ftp_rawlist($this->connect, $path) as $str) { |
||
791 | $info = preg_split('/\s+/', $str, 9); |
||
792 | View Code Duplication | if (!isset($this->ftpOsUnix)) { |
|
793 | $this->ftpOsUnix = !preg_match('/\d/', substr($info[0], 0, 1)); |
||
794 | } |
||
795 | if (!$this->ftpOsUnix) { |
||
796 | $info = $this->normalizeRawWindows($str); |
||
797 | } |
||
798 | $name = isset($info[8])? trim($info[8]) : ''; |
||
799 | if ($name && $name !== '.' && $name !== '..' && substr(strtolower($info[0]), 0, 1) === 'd') { |
||
800 | return true; |
||
801 | } |
||
802 | } |
||
803 | return false; |
||
804 | } |
||
805 | |||
806 | /** |
||
807 | * Return object width and height |
||
808 | * Ususaly used for images, but can be realize for video etc... |
||
809 | * |
||
810 | * @param string $path file path |
||
811 | * @param string $mime file mime type |
||
812 | * @return string|false |
||
813 | * @author Dmitry (dio) Levashov |
||
814 | **/ |
||
815 | View Code Duplication | protected function _dimensions($path, $mime) { |
|
816 | $ret = false; |
||
817 | if ($imgsize = $this->getImageSize($path, $mime)) { |
||
818 | $ret = $imgsize['dimensions']; |
||
819 | } |
||
820 | return $ret; |
||
821 | } |
||
822 | |||
823 | /******************** file/dir content *********************/ |
||
824 | |||
825 | /** |
||
826 | * Return files list in directory. |
||
827 | * |
||
828 | * @param string $path dir path |
||
829 | * @return array |
||
830 | * @author Dmitry (dio) Levashov |
||
831 | * @author Cem (DiscoFever) |
||
832 | **/ |
||
833 | protected function _scandir($path) { |
||
834 | $files = array(); |
||
835 | |||
836 | foreach (ftp_rawlist($this->connect, $path) as $str) { |
||
837 | if (($stat = $this->parseRaw($str, $path, true))) { |
||
838 | $files[] = $this->_joinPath($path, $stat['name']); |
||
839 | } |
||
840 | } |
||
841 | |||
842 | return $files; |
||
843 | } |
||
844 | |||
845 | /** |
||
846 | * Open file and return file pointer |
||
847 | * |
||
848 | * @param string $path file path |
||
849 | * @param bool $write open file for writing |
||
850 | * @return resource|false |
||
851 | * @author Dmitry (dio) Levashov |
||
852 | **/ |
||
853 | protected function _fopen($path, $mode='rb') { |
||
854 | // try ftp stream wrapper |
||
855 | if (ini_get('allow_url_fopen')) { |
||
856 | $url = 'ftp://'.$this->options['user'].':'.$this->options['pass'].'@'.$this->options['host'].':'.$this->options['port'].$path; |
||
857 | if (strtolower($mode[0]) === 'w') { |
||
858 | $context = stream_context_create(array('ftp' => array('overwrite' => true))); |
||
859 | $fp = @fopen($url, $mode, false, $context); |
||
860 | } else { |
||
861 | $fp = @fopen($url, $mode); |
||
862 | } |
||
863 | if ($fp) { |
||
864 | return $fp; |
||
865 | } |
||
866 | } |
||
867 | |||
868 | if ($this->tmp) { |
||
869 | $local = $this->getTempFile($path); |
||
870 | $fp = @fopen($local, 'wb'); |
||
871 | if (ftp_fget($this->connect, $fp, $path, FTP_BINARY)) { |
||
872 | fclose($fp); |
||
873 | $fp = fopen($local, $mode); |
||
874 | return $fp; |
||
875 | } |
||
876 | @fclose($fp); |
||
877 | is_file($local) && @unlink($local); |
||
878 | } |
||
879 | |||
880 | return false; |
||
881 | } |
||
882 | |||
883 | /** |
||
884 | * Close opened file |
||
885 | * |
||
886 | * @param resource $fp file pointer |
||
887 | * @return bool |
||
888 | * @author Dmitry (dio) Levashov |
||
889 | **/ |
||
890 | protected function _fclose($fp, $path='') { |
||
891 | @fclose($fp); |
||
0 ignored issues
–
show
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.');
}
![]() |
|||
892 | if ($path) { |
||
893 | @unlink($this->getTempFile($path)); |
||
894 | } |
||
895 | } |
||
896 | |||
897 | /******************** file/dir manipulations *************************/ |
||
898 | |||
899 | /** |
||
900 | * Create dir and return created dir path or false on failed |
||
901 | * |
||
902 | * @param string $path parent dir path |
||
903 | * @param string $name new directory name |
||
904 | * @return string|bool |
||
905 | * @author Dmitry (dio) Levashov |
||
906 | **/ |
||
907 | protected function _mkdir($path, $name) { |
||
908 | $path = $this->_joinPath($path, $name); |
||
909 | if (ftp_mkdir($this->connect, $path) === false) { |
||
910 | return false; |
||
911 | } |
||
912 | |||
913 | $this->options['dirMode'] && @ftp_chmod($this->connect, $this->options['dirMode'], $path); |
||
914 | return $path; |
||
915 | } |
||
916 | |||
917 | /** |
||
918 | * Create file and return it's path or false on failed |
||
919 | * |
||
920 | * @param string $path parent dir path |
||
921 | * @param string $name new file name |
||
922 | * @return string|bool |
||
923 | * @author Dmitry (dio) Levashov |
||
924 | **/ |
||
925 | protected function _mkfile($path, $name) { |
||
926 | if ($this->tmp) { |
||
927 | $path = $this->_joinPath($path, $name); |
||
928 | $local = $this->getTempFile(); |
||
929 | $res = touch($local) && ftp_put($this->connect, $path, $local, FTP_ASCII); |
||
930 | @unlink($local); |
||
0 ignored issues
–
show
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.');
}
![]() |
|||
931 | return $res ? $path : false; |
||
932 | } |
||
933 | return false; |
||
934 | } |
||
935 | |||
936 | /** |
||
937 | * Create symlink. FTP driver does not support symlinks. |
||
938 | * |
||
939 | * @param string $target link target |
||
940 | * @param string $path symlink path |
||
941 | * @return bool |
||
942 | * @author Dmitry (dio) Levashov |
||
943 | **/ |
||
944 | protected function _symlink($target, $path, $name) { |
||
945 | return false; |
||
946 | } |
||
947 | |||
948 | /** |
||
949 | * Copy file into another file |
||
950 | * |
||
951 | * @param string $source source file path |
||
952 | * @param string $targetDir target directory path |
||
953 | * @param string $name new file name |
||
954 | * @return bool |
||
955 | * @author Dmitry (dio) Levashov |
||
956 | **/ |
||
957 | protected function _copy($source, $targetDir, $name) { |
||
958 | $res = false; |
||
959 | |||
960 | if ($this->tmp) { |
||
961 | $local = $this->getTempFile(); |
||
962 | $target = $this->_joinPath($targetDir, $name); |
||
963 | |||
964 | View Code Duplication | if (ftp_get($this->connect, $local, $source, FTP_BINARY) |
|
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. ![]() |
|||
965 | && ftp_put($this->connect, $target, $local, $this->ftpMode($target))) { |
||
966 | $res = $target; |
||
967 | } |
||
968 | @unlink($local); |
||
0 ignored issues
–
show
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.');
}
![]() |
|||
969 | } |
||
970 | |||
971 | return $res; |
||
972 | } |
||
973 | |||
974 | /** |
||
975 | * Move file into another parent dir. |
||
976 | * Return new file path or false. |
||
977 | * |
||
978 | * @param string $source source file path |
||
979 | * @param string $target target dir path |
||
980 | * @param string $name file name |
||
981 | * @return string|bool |
||
982 | * @author Dmitry (dio) Levashov |
||
983 | **/ |
||
984 | protected function _move($source, $targetDir, $name) { |
||
985 | $target = $this->_joinPath($targetDir, $name); |
||
986 | return ftp_rename($this->connect, $source, $target) ? $target : false; |
||
987 | } |
||
988 | |||
989 | /** |
||
990 | * Remove file |
||
991 | * |
||
992 | * @param string $path file path |
||
993 | * @return bool |
||
994 | * @author Dmitry (dio) Levashov |
||
995 | **/ |
||
996 | protected function _unlink($path) { |
||
997 | return ftp_delete($this->connect, $path); |
||
998 | } |
||
999 | |||
1000 | /** |
||
1001 | * Remove dir |
||
1002 | * |
||
1003 | * @param string $path dir path |
||
1004 | * @return bool |
||
1005 | * @author Dmitry (dio) Levashov |
||
1006 | **/ |
||
1007 | protected function _rmdir($path) { |
||
1008 | return ftp_rmdir($this->connect, $path); |
||
1009 | } |
||
1010 | |||
1011 | /** |
||
1012 | * Create new file and write into it from file pointer. |
||
1013 | * Return new file path or false on error. |
||
1014 | * |
||
1015 | * @param resource $fp file pointer |
||
1016 | * @param string $dir target dir path |
||
1017 | * @param string $name file name |
||
1018 | * @param array $stat file stat (required by some virtual fs) |
||
1019 | * @return bool|string |
||
1020 | * @author Dmitry (dio) Levashov |
||
1021 | **/ |
||
1022 | protected function _save($fp, $dir, $name, $stat) { |
||
1023 | $path = $this->_joinPath($dir, $name); |
||
1024 | return ftp_fput($this->connect, $path, $fp, $this->ftpMode($path)) |
||
1025 | ? $path |
||
1026 | : false; |
||
1027 | } |
||
1028 | |||
1029 | /** |
||
1030 | * Get file contents |
||
1031 | * |
||
1032 | * @param string $path file path |
||
1033 | * @return string|false |
||
1034 | * @author Dmitry (dio) Levashov |
||
1035 | **/ |
||
1036 | protected function _getContents($path) { |
||
1037 | $contents = ''; |
||
1038 | if (($fp = $this->_fopen($path))) { |
||
1039 | while (!feof($fp)) { |
||
1040 | $contents .= fread($fp, 8192); |
||
1041 | } |
||
1042 | $this->_fclose($fp, $path); |
||
1043 | return $contents; |
||
1044 | } |
||
1045 | return false; |
||
1046 | } |
||
1047 | |||
1048 | /** |
||
1049 | * Write a string to a file |
||
1050 | * |
||
1051 | * @param string $path file path |
||
1052 | * @param string $content new file content |
||
1053 | * @return bool |
||
1054 | * @author Dmitry (dio) Levashov |
||
1055 | **/ |
||
1056 | protected function _filePutContents($path, $content) { |
||
1057 | $res = false; |
||
1058 | |||
1059 | if ($this->tmp) { |
||
1060 | $local = $this->getTempFile(); |
||
1061 | |||
1062 | View Code Duplication | if (@file_put_contents($local, $content, LOCK_EX) !== 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. ![]() |
|||
1063 | && ($fp = @fopen($local, 'rb'))) { |
||
1064 | clearstatcache(); |
||
1065 | $res = ftp_fput($this->connect, $path, $fp, $this->ftpMode($path)); |
||
1066 | @fclose($fp); |
||
0 ignored issues
–
show
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.');
}
![]() |
|||
1067 | } |
||
1068 | file_exists($local) && @unlink($local); |
||
1069 | } |
||
1070 | |||
1071 | return $res; |
||
1072 | } |
||
1073 | |||
1074 | /** |
||
1075 | * Detect available archivers |
||
1076 | * |
||
1077 | * @return void |
||
1078 | **/ |
||
1079 | protected function _checkArchivers() { |
||
1080 | $this->archivers = $this->getArchivers(); |
||
1081 | return; |
||
1082 | } |
||
1083 | |||
1084 | /** |
||
1085 | * chmod availability |
||
1086 | * |
||
1087 | * @return bool |
||
1088 | **/ |
||
1089 | protected function _chmod($path, $mode) { |
||
1090 | $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o",$mode)); |
||
1091 | return @ftp_chmod($this->connect, $modeOct, $path); |
||
1092 | } |
||
1093 | |||
1094 | /** |
||
1095 | * Recursive symlinks search |
||
1096 | * |
||
1097 | * @param string $path file/dir path |
||
1098 | * @return bool |
||
1099 | * @author Dmitry (dio) Levashov |
||
1100 | **/ |
||
1101 | protected function _findSymlinks($path) { |
||
1102 | if (is_link($path)) { |
||
1103 | return true; |
||
1104 | } |
||
1105 | if (is_dir($path)) { |
||
1106 | View Code Duplication | foreach (scandir($path) as $name) { |
|
1107 | if ($name != '.' && $name != '..') { |
||
1108 | $p = $path.DIRECTORY_SEPARATOR.$name; |
||
1109 | if (is_link($p)) { |
||
1110 | return true; |
||
1111 | } |
||
1112 | if (is_dir($p) && $this->_findSymlinks($p)) { |
||
1113 | return true; |
||
1114 | } elseif (is_file($p)) { |
||
1115 | $this->archiveSize += sprintf('%u', filesize($p)); |
||
1116 | } |
||
1117 | } |
||
1118 | } |
||
1119 | } else { |
||
1120 | $this->archiveSize += sprintf('%u', filesize($path)); |
||
1121 | } |
||
1122 | |||
1123 | return false; |
||
1124 | } |
||
1125 | |||
1126 | /** |
||
1127 | * Extract files from archive |
||
1128 | * |
||
1129 | * @param string $path archive path |
||
1130 | * @param array $arc archiver command and arguments (same as in $this->archivers) |
||
1131 | * @return true |
||
1132 | * @author Dmitry (dio) Levashov, |
||
1133 | * @author Alexey Sukhotin |
||
1134 | **/ |
||
1135 | protected function _extract($path, $arc) |
||
1136 | { |
||
1137 | $dir = $this->tempDir(); |
||
1138 | if (!$dir) { |
||
1139 | return false; |
||
1140 | } |
||
1141 | |||
1142 | $basename = $this->_basename($path); |
||
1143 | $localPath = $dir . DIRECTORY_SEPARATOR . $basename; |
||
1144 | |||
1145 | if (!ftp_get($this->connect, $localPath, $path, FTP_BINARY)) { |
||
1146 | //cleanup |
||
1147 | $this->rmdirRecursive($dir); |
||
1148 | return false; |
||
1149 | } |
||
1150 | |||
1151 | $this->unpackArchive($localPath, $arc); |
||
1152 | |||
1153 | $filesToProcess = elFinderVolumeFTP::listFilesInDirectory($dir, true); |
||
1154 | |||
1155 | // no files - extract error ? |
||
1156 | if (empty($filesToProcess)) { |
||
1157 | return false; |
||
1158 | } |
||
1159 | |||
1160 | $this->archiveSize = 0; |
||
1161 | |||
1162 | // find symlinks |
||
1163 | $symlinks = $this->_findSymlinks($dir); |
||
1164 | |||
1165 | View Code Duplication | if ($symlinks) { |
|
1166 | $this->rmdirRecursive($dir); |
||
1167 | return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS))); |
||
1168 | } |
||
1169 | |||
1170 | // check max files size |
||
1171 | View Code Duplication | if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) { |
|
1172 | $this->rmdirRecursive($dir); |
||
1173 | return $this->setError(elFinder::ERROR_ARC_MAXSIZE); |
||
1174 | } |
||
1175 | |||
1176 | $extractTo = $this->extractToNewdir; // 'auto', ture or false |
||
1177 | |||
1178 | // archive contains one item - extract in archive dir |
||
1179 | $name = ''; |
||
1180 | $src = $dir . DIRECTORY_SEPARATOR . $filesToProcess[0]; |
||
1181 | if (($extractTo === 'auto' || !$extractTo) && count($filesToProcess) === 1 && is_file($src)) { |
||
1182 | $name = $filesToProcess[0]; |
||
1183 | } else if ($extractTo === 'auto' || $extractTo) { |
||
1184 | // for several files - create new directory |
||
1185 | // create unique name for directory |
||
1186 | $src = $dir; |
||
1187 | $name = basename($path); |
||
1188 | View Code Duplication | if (preg_match('/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/i', $name, $m)) { |
|
1189 | $name = substr($name, 0, strlen($name)-strlen($m[0])); |
||
1190 | } |
||
1191 | $test = $this->_joinPath(dirname($path), $name); |
||
1192 | if ($this->stat($test)) { |
||
1193 | $name = $this->uniqueName(dirname($path), $name, '-', false); |
||
1194 | } |
||
1195 | } |
||
1196 | |||
1197 | if ($name !== '' && is_file($src)) { |
||
1198 | $result = $this->_joinPath(dirname($path), $name); |
||
1199 | |||
1200 | if (! ftp_put($this->connect, $result, $src, FTP_BINARY)) { |
||
1201 | $this->rmdirRecursive($dir); |
||
1202 | return false; |
||
1203 | } |
||
1204 | } else { |
||
1205 | $dstDir = $this->_dirname($path); |
||
1206 | $result = array(); |
||
1207 | if (is_dir($src)) { |
||
1208 | if (!$dstDir = $this->_mkdir($dstDir, $name)) { |
||
1209 | $this->rmdirRecursive($dir); |
||
1210 | return false; |
||
1211 | } |
||
1212 | $result[] = $dstDir; |
||
1213 | } |
||
1214 | foreach($filesToProcess as $name) { |
||
1215 | $name = rtrim($name, DIRECTORY_SEPARATOR); |
||
1216 | $src = $dir . DIRECTORY_SEPARATOR . $name; |
||
1217 | if (is_dir($src)) { |
||
1218 | $p = dirname($name); |
||
1219 | $name = basename($name); |
||
1220 | if (! $target = $this->_mkdir($this->_joinPath($dstDir, $p), $name)) { |
||
1221 | $this->rmdirRecursive($dir); |
||
1222 | return false; |
||
1223 | } |
||
1224 | View Code Duplication | } else { |
|
1225 | $target = $this->_joinPath($dstDir, $name); |
||
1226 | if (! ftp_put($this->connect, $target, $src, FTP_BINARY)) { |
||
1227 | $this->rmdirRecursive($dir); |
||
1228 | return false; |
||
1229 | } |
||
1230 | } |
||
1231 | $result[] = $target; |
||
1232 | } |
||
1233 | if (!$result) { |
||
1234 | $this->rmdirRecursive($dir); |
||
1235 | return false; |
||
1236 | } |
||
1237 | } |
||
1238 | |||
1239 | is_dir($dir) && $this->rmdirRecursive($dir); |
||
1240 | |||
1241 | $this->clearcache(); |
||
1242 | return $result? $result : false; |
||
1243 | } |
||
1244 | |||
1245 | /** |
||
1246 | * Create archive and return its path |
||
1247 | * |
||
1248 | * @param string $dir target dir |
||
1249 | * @param array $files files names list |
||
1250 | * @param string $name archive name |
||
1251 | * @param array $arc archiver options |
||
1252 | * @return string|bool |
||
1253 | * @author Dmitry (dio) Levashov, |
||
1254 | * @author Alexey Sukhotin |
||
1255 | **/ |
||
1256 | protected function _archive($dir, $files, $name, $arc) |
||
1257 | { |
||
1258 | // get current directory |
||
1259 | $cwd = getcwd(); |
||
1260 | |||
1261 | $tmpDir = $this->tempDir(); |
||
1262 | if (!$tmpDir) { |
||
1263 | return false; |
||
1264 | } |
||
1265 | |||
1266 | //download data |
||
1267 | if (!$this->ftp_download_files($dir, $files, $tmpDir)) { |
||
1268 | //cleanup |
||
1269 | $this->rmdirRecursive($tmpDir); |
||
1270 | return false; |
||
1271 | } |
||
1272 | |||
1273 | $remoteArchiveFile = false; |
||
1274 | if ($path = $this->makeArchive($tmpDir, $files, $name, $arc)) { |
||
1275 | $remoteArchiveFile = $this->_joinPath($dir, $name); |
||
1276 | if (!ftp_put($this->connect, $remoteArchiveFile, $path, FTP_BINARY)) { |
||
1277 | $remoteArchiveFile = false; |
||
1278 | } |
||
1279 | } |
||
1280 | |||
1281 | //cleanup |
||
1282 | if(!$this->rmdirRecursive($tmpDir)) { |
||
1283 | return false; |
||
1284 | } |
||
1285 | |||
1286 | return $remoteArchiveFile; |
||
1287 | } |
||
1288 | |||
1289 | /** |
||
1290 | * Create writable temporary directory and return path to it. |
||
1291 | * @return string path to the new temporary directory or false in case of error. |
||
1292 | */ |
||
1293 | private function tempDir() |
||
1294 | { |
||
1295 | $tempPath = tempnam($this->tmp, 'elFinder'); |
||
1296 | if (!$tempPath) { |
||
1297 | $this->setError(elFinder::ERROR_CREATING_TEMP_DIR, $this->tmp); |
||
1298 | return false; |
||
1299 | } |
||
1300 | $success = unlink($tempPath); |
||
1301 | if (!$success) { |
||
1302 | $this->setError(elFinder::ERROR_CREATING_TEMP_DIR, $this->tmp); |
||
1303 | return false; |
||
1304 | } |
||
1305 | $success = mkdir($tempPath, 0700, true); |
||
1306 | if (!$success) { |
||
1307 | $this->setError(elFinder::ERROR_CREATING_TEMP_DIR, $this->tmp); |
||
1308 | return false; |
||
1309 | } |
||
1310 | return $tempPath; |
||
1311 | } |
||
1312 | |||
1313 | /** |
||
1314 | * Gets an array of absolute remote FTP paths of files and |
||
1315 | * folders in $remote_directory omitting symbolic links. |
||
1316 | * |
||
1317 | * @param $remote_directory string remote FTP path to scan for file and folders recursively |
||
1318 | * @param $targets array Array of target item. `null` is to get all of items |
||
1319 | * @return array of elements each of which is an array of two elements: |
||
1320 | * <ul> |
||
1321 | * <li>$item['path'] - absolute remote FTP path</li> |
||
1322 | * <li>$item['type'] - either 'f' for file or 'd' for directory</li> |
||
1323 | * </ul> |
||
1324 | */ |
||
1325 | protected function ftp_scan_dir($remote_directory, $targets = null) |
||
1326 | { |
||
1327 | $buff = ftp_rawlist($this->connect, $remote_directory); |
||
1328 | $items = array(); |
||
1329 | if ($targets && is_array($targets)) { |
||
1330 | $targets = array_flip($targets); |
||
1331 | } else { |
||
1332 | $targets = false; |
||
1333 | } |
||
1334 | foreach ($buff as $str) { |
||
1335 | $info = preg_split("/\s+/", $str, 9); |
||
1336 | View Code Duplication | if (!isset($this->ftpOsUnix)) { |
|
1337 | $this->ftpOsUnix = !preg_match('/\d/', substr($info[0], 0, 1)); |
||
1338 | } |
||
1339 | if (!$this->ftpOsUnix) { |
||
1340 | $info = $this->normalizeRawWindows($str); |
||
1341 | } |
||
1342 | $type = substr($info[0], 0, 1); |
||
1343 | $name = trim($info[8]); |
||
1344 | if ($name !== '.' && $name !== '..' && (!$targets || isset($targets[$name]))) { |
||
1345 | switch ($type) { |
||
1346 | case 'l' : //omit symbolic links |
||
1347 | case 'd' : |
||
1348 | $remote_file_path = $this->_joinPath($remote_directory, $name); |
||
1349 | $item = array(); |
||
1350 | $item['path'] = $remote_file_path; |
||
1351 | $item['type'] = 'd'; // normal file |
||
1352 | $items[] = $item; |
||
1353 | $items = array_merge($items, $this->ftp_scan_dir($remote_file_path)); |
||
1354 | break; |
||
1355 | default: |
||
1356 | $remote_file_path = $this->_joinPath($remote_directory, $name); |
||
1357 | $item = array(); |
||
1358 | $item['path'] = $remote_file_path; |
||
1359 | $item['type'] = 'f'; // normal file |
||
1360 | $items[] = $item; |
||
1361 | } |
||
1362 | } |
||
1363 | } |
||
1364 | return $items; |
||
1365 | } |
||
1366 | |||
1367 | /** |
||
1368 | * Downloads specified files from remote directory |
||
1369 | * if there is a directory among files it is downloaded recursively (omitting symbolic links). |
||
1370 | * |
||
1371 | * @param $remote_directory string remote FTP path to a source directory to download from. |
||
1372 | * @param array $files list of files to download from remote directory. |
||
1373 | * @param $dest_local_directory string destination folder to store downloaded files. |
||
1374 | * @return bool true on success and false on failure. |
||
1375 | */ |
||
1376 | private function ftp_download_files($remote_directory, array $files, $dest_local_directory) |
||
1377 | { |
||
1378 | $contents = $this->ftp_scan_dir($remote_directory, $files); |
||
1379 | if (!isset($contents)) { |
||
1380 | $this->setError(elFinder::ERROR_FTP_DOWNLOAD_FILE, $remote_directory); |
||
1381 | return false; |
||
1382 | } |
||
1383 | $remoteDirLen = strlen($remote_directory); |
||
1384 | foreach ($contents as $item) { |
||
1385 | $relative_path = substr($item['path'], $remoteDirLen); |
||
1386 | $local_path = $dest_local_directory . DIRECTORY_SEPARATOR . $relative_path; |
||
1387 | switch ($item['type']) { |
||
1388 | case 'd': |
||
1389 | $success = mkdir($local_path); |
||
1390 | break; |
||
1391 | case 'f': |
||
1392 | $success = ftp_get($this->connect, $local_path, $item['path'], FTP_BINARY); |
||
1393 | break; |
||
1394 | default: |
||
1395 | $success = true; |
||
1396 | } |
||
1397 | if (!$success) { |
||
1398 | $this->setError(elFinder::ERROR_FTP_DOWNLOAD_FILE, $remote_directory); |
||
1399 | return false; |
||
1400 | } |
||
1401 | } |
||
1402 | return true; |
||
1403 | } |
||
1404 | |||
1405 | /** |
||
1406 | * Delete local directory recursively. |
||
1407 | * @param $dirPath string to directory to be erased. |
||
1408 | * @return bool true on success and false on failure. |
||
1409 | */ |
||
1410 | private function deleteDir($dirPath) |
||
1411 | { |
||
1412 | if (!is_dir($dirPath)) { |
||
1413 | $success = unlink($dirPath); |
||
1414 | } else { |
||
1415 | $success = true; |
||
1416 | foreach (array_reverse(elFinderVolumeFTP::listFilesInDirectory($dirPath, false)) as $path) { |
||
1417 | $path = $dirPath . DIRECTORY_SEPARATOR . $path; |
||
1418 | if(is_link($path)) { |
||
1419 | unlink($path); |
||
1420 | } else if (is_dir($path)) { |
||
1421 | $success = rmdir($path); |
||
1422 | } else { |
||
1423 | $success = unlink($path); |
||
1424 | } |
||
1425 | if (!$success) { |
||
1426 | break; |
||
1427 | } |
||
1428 | } |
||
1429 | if($success) { |
||
1430 | $success = rmdir($dirPath); |
||
1431 | } |
||
1432 | } |
||
1433 | if(!$success) { |
||
1434 | $this->setError(elFinder::ERROR_RM, $dirPath); |
||
1435 | return false; |
||
1436 | } |
||
1437 | return $success; |
||
1438 | } |
||
1439 | |||
1440 | /** |
||
1441 | * Returns array of strings containing all files and folders in the specified local directory. |
||
1442 | * @param $dir |
||
1443 | * @param string $prefix |
||
1444 | * @internal param string $path path to directory to scan. |
||
1445 | * @return array array of files and folders names relative to the $path |
||
1446 | * or an empty array if the directory $path is empty, |
||
1447 | * <br /> |
||
1448 | * false if $path is not a directory or does not exist. |
||
1449 | */ |
||
1450 | private static function listFilesInDirectory($dir, $omitSymlinks, $prefix = '') |
||
1451 | { |
||
1452 | if (!is_dir($dir)) { |
||
1453 | return false; |
||
1454 | } |
||
1455 | $excludes = array(".",".."); |
||
1456 | $result = array(); |
||
1457 | $files = scandir($dir); |
||
1458 | if(!$files) { |
||
1459 | return array(); |
||
1460 | } |
||
1461 | foreach($files as $file) { |
||
1462 | if(!in_array($file, $excludes)) { |
||
1463 | $path = $dir.DIRECTORY_SEPARATOR.$file; |
||
1464 | if(is_link($path)) { |
||
1465 | if($omitSymlinks) { |
||
1466 | continue; |
||
1467 | } else { |
||
1468 | $result[] = $prefix.$file; |
||
1469 | } |
||
1470 | } else if(is_dir($path)) { |
||
1471 | $result[] = $prefix.$file.DIRECTORY_SEPARATOR; |
||
1472 | $subs = elFinderVolumeFTP::listFilesInDirectory($path, $omitSymlinks, $prefix.$file.DIRECTORY_SEPARATOR); |
||
1473 | if($subs) { |
||
1474 | $result = array_merge($result, $subs); |
||
1475 | } |
||
1476 | |||
1477 | } else { |
||
1478 | $result[] = $prefix.$file; |
||
1479 | } |
||
1480 | } |
||
1481 | } |
||
1482 | return $result; |
||
1483 | } |
||
1484 | |||
1485 | } // END class |
||
1486 | |||
1487 |
In PHP, under loose comparison (like
==
, or!=
, orswitch
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: