PluginProbe
WPIDE – File Manager & Code Editor / 3.5.9
WPIDE – File Manager & Code Editor v3.5.9
3.5.9 3.5.8 3.5.7 2.0.14 2.0.15 2.0.16 2.0.2 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1 2.2 2.3 2.3.1 2.3.2 2.4.0 2.5 2.6 3.0 3.1 3.2 3.3 All 55 releases
wpide / vendor / league / flysystem / src / Adapter / AbstractFtpAdapter.php

AbstractFtpAdapter.php in WPIDE – File Manager & Code Editor 3.5.9, at vendor/league/flysystem/src/Adapter/AbstractFtpAdapter.php

706 lines 15.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace League\Flysystem\Adapter;
4
5 use DateTime;
6 use League\Flysystem\AdapterInterface;
7 use League\Flysystem\Config;
8 use League\Flysystem\NotSupportedException;
9 use League\Flysystem\SafeStorage;
10 use RuntimeException;
11
12 abstract class AbstractFtpAdapter extends AbstractAdapter
13 {
14 /**
15 * @var mixed
16 */
17 protected $connection;
18
19 /**
20 * @var string
21 */
22 protected $host;
23
24 /**
25 * @var int
26 */
27 protected $port = 21;
28
29 /**
30 * @var bool
31 */
32 protected $ssl = false;
33
34 /**
35 * @var int
36 */
37 protected $timeout = 90;
38
39 /**
40 * @var bool
41 */
42 protected $passive = true;
43
44 /**
45 * @var string
46 */
47 protected $separator = '/';
48
49 /**
50 * @var string|null
51 */
52 protected $root;
53
54 /**
55 * @var int
56 */
57 protected $permPublic = 0744;
58
59 /**
60 * @var int
61 */
62 protected $permPrivate = 0700;
63
64 /**
65 * @var array
66 */
67 protected $configurable = [];
68
69 /**
70 * @var string
71 */
72 protected $systemType;
73
74 /**
75 * @var SafeStorage
76 */
77 protected $safeStorage;
78
79 /**
80 * True to enable timestamps for FTP servers that return unix-style listings.
81 *
82 * @var bool
83 */
84 protected $enableTimestampsOnUnixListings = false;
85
86 /**
87 * Constructor.
88 *
89 * @param array $config
90 */
91 public function __construct(array $config)
92 {
93 $this->safeStorage = new SafeStorage();
94 $this->setConfig($config);
95 }
96
97 /**
98 * Set the config.
99 *
100 * @param array $config
101 *
102 * @return $this
103 */
104 public function setConfig(array $config)
105 {
106 foreach ($this->configurable as $setting) {
107 if ( ! isset($config[$setting])) {
108 continue;
109 }
110
111 $method = 'set' . ucfirst($setting);
112
113 if (method_exists($this, $method)) {
114 $this->$method($config[$setting]);
115 }
116 }
117
118 return $this;
119 }
120
121 /**
122 * Returns the host.
123 *
124 * @return string
125 */
126 public function getHost()
127 {
128 return $this->host;
129 }
130
131 /**
132 * Set the host.
133 *
134 * @param string $host
135 *
136 * @return $this
137 */
138 public function setHost($host)
139 {
140 $this->host = $host;
141
142 return $this;
143 }
144
145 /**
146 * Set the public permission value.
147 *
148 * @param int $permPublic
149 *
150 * @return $this
151 */
152 public function setPermPublic($permPublic)
153 {
154 $this->permPublic = $permPublic;
155
156 return $this;
157 }
158
159 /**
160 * Set the private permission value.
161 *
162 * @param int $permPrivate
163 *
164 * @return $this
165 */
166 public function setPermPrivate($permPrivate)
167 {
168 $this->permPrivate = $permPrivate;
169
170 return $this;
171 }
172
173 /**
174 * Returns the ftp port.
175 *
176 * @return int
177 */
178 public function getPort()
179 {
180 return $this->port;
181 }
182
183 /**
184 * Returns the root folder to work from.
185 *
186 * @return string
187 */
188 public function getRoot()
189 {
190 return $this->root;
191 }
192
193 /**
194 * Set the ftp port.
195 *
196 * @param int|string $port
197 *
198 * @return $this
199 */
200 public function setPort($port)
201 {
202 $this->port = (int) $port;
203
204 return $this;
205 }
206
207 /**
208 * Set the root folder to work from.
209 *
210 * @param string $root
211 *
212 * @return $this
213 */
214 public function setRoot($root)
215 {
216 $this->root = rtrim($root, '\\/') . $this->separator;
217
218 return $this;
219 }
220
221 /**
222 * Returns the ftp username.
223 *
224 * @return string username
225 */
226 public function getUsername()
227 {
228 $username = $this->safeStorage->retrieveSafely('username');
229
230 return $username !== null ? $username : 'anonymous';
231 }
232
233 /**
234 * Set ftp username.
235 *
236 * @param string $username
237 *
238 * @return $this
239 */
240 public function setUsername($username)
241 {
242 $this->safeStorage->storeSafely('username', $username);
243
244 return $this;
245 }
246
247 /**
248 * Returns the password.
249 *
250 * @return string password
251 */
252 public function getPassword()
253 {
254 return $this->safeStorage->retrieveSafely('password');
255 }
256
257 /**
258 * Set the ftp password.
259 *
260 * @param string $password
261 *
262 * @return $this
263 */
264 public function setPassword($password)
265 {
266 $this->safeStorage->storeSafely('password', $password);
267
268 return $this;
269 }
270
271 /**
272 * Returns the amount of seconds before the connection will timeout.
273 *
274 * @return int
275 */
276 public function getTimeout()
277 {
278 return $this->timeout;
279 }
280
281 /**
282 * Set the amount of seconds before the connection should timeout.
283 *
284 * @param int $timeout
285 *
286 * @return $this
287 */
288 public function setTimeout($timeout)
289 {
290 $this->timeout = (int) $timeout;
291
292 return $this;
293 }
294
295 /**
296 * Return the FTP system type.
297 *
298 * @return string
299 */
300 public function getSystemType()
301 {
302 return $this->systemType;
303 }
304
305 /**
306 * Set the FTP system type (windows or unix).
307 *
308 * @param string $systemType
309 *
310 * @return $this
311 */
312 public function setSystemType($systemType)
313 {
314 $this->systemType = strtolower($systemType);
315
316 return $this;
317 }
318
319 /**
320 * True to enable timestamps for FTP servers that return unix-style listings.
321 *
322 * @param bool $bool
323 *
324 * @return $this
325 */
326 public function setEnableTimestampsOnUnixListings($bool = false)
327 {
328 $this->enableTimestampsOnUnixListings = $bool;
329
330 return $this;
331 }
332
333 /**
334 * @inheritdoc
335 */
336 public function listContents($directory = '', $recursive = false)
337 {
338 return $this->listDirectoryContents($directory, $recursive);
339 }
340
341 abstract protected function listDirectoryContents($directory, $recursive = false);
342
343 /**
344 * Normalize a directory listing.
345 *
346 * @param array $listing
347 * @param string $prefix
348 *
349 * @return array directory listing
350 */
351 protected function normalizeListing(array $listing, $prefix = '')
352 {
353 $base = $prefix;
354 $result = [];
355 $listing = $this->removeDotDirectories($listing);
356
357 while ($item = array_shift($listing)) {
358 if (preg_match('#^.*:$#', $item)) {
359 $base = preg_replace('~^\./*|:$~', '', $item);
360 continue;
361 }
362
363 $result[] = $this->normalizeObject($item, $base);
364 }
365
366 return $this->sortListing($result);
367 }
368
369 /**
370 * Sort a directory listing.
371 *
372 * @param array $result
373 *
374 * @return array sorted listing
375 */
376 protected function sortListing(array $result)
377 {
378 $compare = function ($one, $two) {
379 return strnatcmp($one['path'], $two['path']);
380 };
381
382 usort($result, $compare);
383
384 return $result;
385 }
386
387 /**
388 * Normalize a file entry.
389 *
390 * @param string $item
391 * @param string $base
392 *
393 * @return array normalized file array
394 *
395 * @throws NotSupportedException
396 */
397 protected function normalizeObject($item, $base)
398 {
399 $systemType = $this->systemType ?: $this->detectSystemType($item);
400
401 if ($systemType === 'unix') {
402 return $this->normalizeUnixObject($item, $base);
403 } elseif ($systemType === 'windows') {
404 return $this->normalizeWindowsObject($item, $base);
405 }
406
407 throw NotSupportedException::forFtpSystemType($systemType);
408 }
409
410 /**
411 * Normalize a Unix file entry.
412 *
413 * Given $item contains:
414 * '-rw-r--r-- 1 ftp ftp 409 Aug 19 09:01 file1.txt'
415 *
416 * This function will return:
417 * [
418 * 'type' => 'file',
419 * 'path' => 'file1.txt',
420 * 'visibility' => 'public',
421 * 'size' => 409,
422 * 'timestamp' => 1566205260
423 * ]
424 *
425 * @param string $item
426 * @param string $base
427 *
428 * @return array normalized file array
429 */
430 protected function normalizeUnixObject($item, $base)
431 {
432 $item = preg_replace('#\s+#', ' ', trim($item), 7);
433
434 if (count(explode(' ', $item, 9)) !== 9) {
435 throw new RuntimeException("Metadata can't be parsed from item '$item' , not enough parts.");
436 }
437
438 list($permissions, /* $number */, /* $owner */, /* $group */, $size, $month, $day, $timeOrYear, $name) = explode(' ', $item, 9);
439 $type = $this->detectType($permissions);
440 $path = $base === '' ? $name : $base . $this->separator . $name;
441
442 if ($type === 'dir') {
443 $result = compact('type', 'path');
444 if ($this->enableTimestampsOnUnixListings) {
445 $timestamp = $this->normalizeUnixTimestamp($month, $day, $timeOrYear);
446 $result += compact('timestamp');
447 }
448
449 return $result;
450 }
451
452 $permissions = $this->normalizePermissions($permissions);
453 $visibility = $permissions & 0044 ? AdapterInterface::VISIBILITY_PUBLIC : AdapterInterface::VISIBILITY_PRIVATE;
454 $size = (int) $size;
455
456 $result = compact('type', 'path', 'visibility', 'size');
457 if ($this->enableTimestampsOnUnixListings) {
458 $timestamp = $this->normalizeUnixTimestamp($month, $day, $timeOrYear);
459 $result += compact('timestamp');
460 }
461
462 return $result;
463 }
464
465 /**
466 * Only accurate to the minute (current year), or to the day.
467 *
468 * Inadequacies in timestamp accuracy are due to limitations of the FTP 'LIST' command
469 *
470 * Note: The 'MLSD' command is a machine-readable replacement for 'LIST'
471 * but many FTP servers do not support it :(
472 *
473 * @param string $month e.g. 'Aug'
474 * @param string $day e.g. '19'
475 * @param string $timeOrYear e.g. '09:01' OR '2015'
476 *
477 * @return int
478 */
479 protected function normalizeUnixTimestamp($month, $day, $timeOrYear)
480 {
481 if (is_numeric($timeOrYear)) {
482 $year = $timeOrYear;
483 $hour = '00';
484 $minute = '00';
485 $seconds = '00';
486 } else {
487 $year = date('Y');
488 list($hour, $minute) = explode(':', $timeOrYear);
489 $seconds = '00';
490 }
491 $dateTime = DateTime::createFromFormat('Y-M-j-G:i:s', "{$year}-{$month}-{$day}-{$hour}:{$minute}:{$seconds}");
492
493 return $dateTime->getTimestamp();
494 }
495
496 /**
497 * Normalize a Windows/DOS file entry.
498 *
499 * @param string $item
500 * @param string $base
501 *
502 * @return array normalized file array
503 */
504 protected function normalizeWindowsObject($item, $base)
505 {
506 $item = preg_replace('#\s+#', ' ', trim($item), 3);
507
508 if (count(explode(' ', $item, 4)) !== 4) {
509 throw new RuntimeException("Metadata can't be parsed from item '$item' , not enough parts.");
510 }
511
512 list($date, $time, $size, $name) = explode(' ', $item, 4);
513 $path = $base === '' ? $name : $base . $this->separator . $name;
514
515 // Check for the correct date/time format
516 $format = strlen($date) === 8 ? 'm-d-yH:iA' : 'Y-m-dH:i';
517 $dt = DateTime::createFromFormat($format, $date . $time);
518 $timestamp = $dt ? $dt->getTimestamp() : (int) strtotime("$date $time");
519
520 if ($size === '<DIR>') {
521 $type = 'dir';
522
523 return compact('type', 'path', 'timestamp');
524 }
525
526 $type = 'file';
527 $visibility = AdapterInterface::VISIBILITY_PUBLIC;
528 $size = (int) $size;
529
530 return compact('type', 'path', 'visibility', 'size', 'timestamp');
531 }
532
533 /**
534 * Get the system type from a listing item.
535 *
536 * @param string $item
537 *
538 * @return string the system type
539 */
540 protected function detectSystemType($item)
541 {
542 return preg_match('/^[0-9]{2,4}-[0-9]{2}-[0-9]{2}/', trim($item)) ? 'windows' : 'unix';
543 }
544
545 /**
546 * Get the file type from the permissions.
547 *
548 * @param string $permissions
549 *
550 * @return string file type
551 */
552 protected function detectType($permissions)
553 {
554 return substr($permissions, 0, 1) === 'd' ? 'dir' : 'file';
555 }
556
557 /**
558 * Normalize a permissions string.
559 *
560 * @param string $permissions
561 *
562 * @return int
563 */
564 protected function normalizePermissions($permissions)
565 {
566 if (is_numeric($permissions)) {
567 return ((int) $permissions) & 0777;
568 }
569
570 // remove the type identifier
571 $permissions = substr($permissions, 1);
572
573 // map the string rights to the numeric counterparts
574 $map = ['-' => '0', 'r' => '4', 'w' => '2', 'x' => '1'];
575 $permissions = strtr($permissions, $map);
576
577 // split up the permission groups
578 $parts = str_split($permissions, 3);
579
580 // convert the groups
581 $mapper = function ($part) {
582 return array_sum(str_split($part));
583 };
584
585 // converts to decimal number
586 return octdec(implode('', array_map($mapper, $parts)));
587 }
588
589 /**
590 * Filter out dot-directories.
591 *
592 * @param array $list
593 *
594 * @return array
595 */
596 public function removeDotDirectories(array $list)
597 {
598 $filter = function ($line) {
599 return $line !== '' && ! preg_match('#.* \.(\.)?$|^total#', $line);
600 };
601
602 return array_filter($list, $filter);
603 }
604
605 /**
606 * @inheritdoc
607 */
608 public function has($path)
609 {
610 return $this->getMetadata($path);
611 }
612
613 /**
614 * @inheritdoc
615 */
616 public function getSize($path)
617 {
618 return $this->getMetadata($path);
619 }
620
621 /**
622 * @inheritdoc
623 */
624 public function getVisibility($path)
625 {
626 return $this->getMetadata($path);
627 }
628
629 /**
630 * Ensure a directory exists.
631 *
632 * @param string $dirname
633 */
634 public function ensureDirectory($dirname)
635 {
636 $dirname = (string) $dirname;
637
638 if ($dirname !== '' && ! $this->has($dirname)) {
639 $this->createDir($dirname, new Config());
640 }
641 }
642
643 /**
644 * @return mixed
645 */
646 public function getConnection()
647 {
648 if ( ! $this->isConnected()) {
649 $this->disconnect();
650 $this->connect();
651 }
652
653 return $this->connection;
654 }
655
656 /**
657 * Get the public permission value.
658 *
659 * @return int
660 */
661 public function getPermPublic()
662 {
663 return $this->permPublic;
664 }
665
666 /**
667 * Get the private permission value.
668 *
669 * @return int
670 */
671 public function getPermPrivate()
672 {
673 return $this->permPrivate;
674 }
675
676 /**
677 * Disconnect on destruction.
678 */
679 public function __destruct()
680 {
681 $this->disconnect();
682 }
683
684 /**
685 * Establish a connection.
686 */
687 abstract public function connect();
688
689 /**
690 * Close the connection.
691 */
692 abstract public function disconnect();
693
694 /**
695 * Check if a connection is active.
696 *
697 * @return bool
698 */
699 abstract public function isConnected();
700
701 protected function escapePath($path)
702 {
703 return str_replace(['*', '[', ']'], ['\\*', '\\[', '\\]'], $path);
704 }
705 }
706