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 / Ftp.php

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

585 lines 13.7 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 League\Flysystem\Adapter\Polyfill\StreamedCopyTrait;
6 use League\Flysystem\AdapterInterface;
7 use League\Flysystem\Config;
8 use League\Flysystem\ConnectionErrorException;
9 use League\Flysystem\ConnectionRuntimeException;
10 use League\Flysystem\InvalidRootException;
11 use League\Flysystem\Util;
12 use League\Flysystem\Util\MimeType;
13
14 use function in_array;
15
16 class Ftp extends AbstractFtpAdapter
17 {
18 use StreamedCopyTrait;
19
20 /**
21 * @var int
22 */
23 protected $transferMode = FTP_BINARY;
24
25 /**
26 * @var null|bool
27 */
28 protected $ignorePassiveAddress = null;
29
30 /**
31 * @var bool
32 */
33 protected $recurseManually = false;
34
35 /**
36 * @var bool
37 */
38 protected $utf8 = false;
39
40 /**
41 * @var array
42 */
43 protected $configurable = [
44 'host',
45 'port',
46 'username',
47 'password',
48 'ssl',
49 'timeout',
50 'root',
51 'permPrivate',
52 'permPublic',
53 'passive',
54 'transferMode',
55 'systemType',
56 'ignorePassiveAddress',
57 'recurseManually',
58 'utf8',
59 'enableTimestampsOnUnixListings',
60 ];
61
62 /**
63 * @var bool
64 */
65 protected $isPureFtpd;
66
67 /**
68 * Set the transfer mode.
69 *
70 * @param int $mode
71 *
72 * @return $this
73 */
74 public function setTransferMode($mode)
75 {
76 $this->transferMode = $mode;
77
78 return $this;
79 }
80
81 /**
82 * Set if Ssl is enabled.
83 *
84 * @param bool $ssl
85 *
86 * @return $this
87 */
88 public function setSsl($ssl)
89 {
90 $this->ssl = (bool) $ssl;
91
92 return $this;
93 }
94
95 /**
96 * Set if passive mode should be used.
97 *
98 * @param bool $passive
99 */
100 public function setPassive($passive = true)
101 {
102 $this->passive = $passive;
103 }
104
105 /**
106 * @param bool $ignorePassiveAddress
107 */
108 public function setIgnorePassiveAddress($ignorePassiveAddress)
109 {
110 $this->ignorePassiveAddress = $ignorePassiveAddress;
111 }
112
113 /**
114 * @param bool $recurseManually
115 */
116 public function setRecurseManually($recurseManually)
117 {
118 $this->recurseManually = $recurseManually;
119 }
120
121 /**
122 * @param bool $utf8
123 */
124 public function setUtf8($utf8)
125 {
126 $this->utf8 = (bool) $utf8;
127 }
128
129 /**
130 * Connect to the FTP server.
131 */
132 public function connect()
133 {
134 $tries = 3;
135 start_connecting:
136
137 if ($this->ssl) {
138 $this->connection = @ftp_ssl_connect($this->getHost(), $this->getPort(), $this->getTimeout());
139 } else {
140 $this->connection = @ftp_connect($this->getHost(), $this->getPort(), $this->getTimeout());
141 }
142
143 if ( ! $this->connection) {
144 $tries--;
145
146 if ($tries > 0) goto start_connecting;
147
148 throw new ConnectionRuntimeException('Could not connect to host: ' . $this->getHost() . ', port:' . $this->getPort());
149 }
150
151 $this->login();
152 $this->setUtf8Mode();
153 $this->setConnectionPassiveMode();
154 $this->setConnectionRoot();
155 $this->isPureFtpd = $this->isPureFtpdServer();
156 }
157
158 /**
159 * Set the connection to UTF-8 mode.
160 */
161 protected function setUtf8Mode()
162 {
163 if ($this->utf8) {
164 $response = ftp_raw($this->connection, "OPTS UTF8 ON");
165 if (!in_array(substr($response[0], 0, 3), ['200', '202'])) {
166 throw new ConnectionRuntimeException(
167 'Could not set UTF-8 mode for connection: ' . $this->getHost() . '::' . $this->getPort()
168 );
169 }
170 }
171 }
172
173 /**
174 * Set the connections to passive mode.
175 *
176 * @throws ConnectionRuntimeException
177 */
178 protected function setConnectionPassiveMode()
179 {
180 if (is_bool($this->ignorePassiveAddress) && defined('FTP_USEPASVADDRESS')) {
181 ftp_set_option($this->connection, FTP_USEPASVADDRESS, ! $this->ignorePassiveAddress);
182 }
183
184 if ( ! ftp_pasv($this->connection, $this->passive)) {
185 throw new ConnectionRuntimeException(
186 'Could not set passive mode for connection: ' . $this->getHost() . '::' . $this->getPort()
187 );
188 }
189 }
190
191 /**
192 * Set the connection root.
193 */
194 protected function setConnectionRoot()
195 {
196 $root = $this->getRoot();
197 $connection = $this->connection;
198
199 if ($root && ! ftp_chdir($connection, $root)) {
200 throw new InvalidRootException('Root is invalid or does not exist: ' . $this->getRoot());
201 }
202
203 // Store absolute path for further reference.
204 // This is needed when creating directories and
205 // initial root was a relative path, else the root
206 // would be relative to the chdir'd path.
207 $this->root = ftp_pwd($connection);
208 }
209
210 /**
211 * Login.
212 *
213 * @throws ConnectionRuntimeException
214 */
215 protected function login()
216 {
217 set_error_handler(function () {
218 });
219 $isLoggedIn = ftp_login(
220 $this->connection,
221 $this->getUsername(),
222 $this->getPassword()
223 );
224 restore_error_handler();
225
226 if ( ! $isLoggedIn) {
227 $this->disconnect();
228 throw new ConnectionRuntimeException(
229 'Could not login with connection: ' . $this->getHost() . '::' . $this->getPort(
230 ) . ', username: ' . $this->getUsername()
231 );
232 }
233 }
234
235 /**
236 * Disconnect from the FTP server.
237 */
238 public function disconnect()
239 {
240 if ($this->hasFtpConnection()) {
241 @ftp_close($this->connection);
242 }
243
244 $this->connection = null;
245 }
246
247 /**
248 * @inheritdoc
249 */
250 public function write($path, $contents, Config $config)
251 {
252 $stream = fopen('php://temp', 'w+b');
253 fwrite($stream, $contents);
254 rewind($stream);
255 $result = $this->writeStream($path, $stream, $config);
256 fclose($stream);
257
258 if ($result === false) {
259 return false;
260 }
261
262 $result['contents'] = $contents;
263 $result['mimetype'] = $config->get('mimetype') ?: Util::guessMimeType($path, $contents);
264
265 return $result;
266 }
267
268 /**
269 * @inheritdoc
270 */
271 public function writeStream($path, $resource, Config $config)
272 {
273 $this->ensureDirectory(Util::dirname($path));
274
275 if ( ! ftp_fput($this->getConnection(), $path, $resource, $this->transferMode)) {
276 return false;
277 }
278
279 if ($visibility = $config->get('visibility')) {
280 $this->setVisibility($path, $visibility);
281 }
282
283 $type = 'file';
284
285 return compact('type', 'path', 'visibility');
286 }
287
288 /**
289 * @inheritdoc
290 */
291 public function update($path, $contents, Config $config)
292 {
293 return $this->write($path, $contents, $config);
294 }
295
296 /**
297 * @inheritdoc
298 */
299 public function updateStream($path, $resource, Config $config)
300 {
301 return $this->writeStream($path, $resource, $config);
302 }
303
304 /**
305 * @inheritdoc
306 */
307 public function rename($path, $newpath)
308 {
309 return ftp_rename($this->getConnection(), $path, $newpath);
310 }
311
312 /**
313 * @inheritdoc
314 */
315 public function delete($path)
316 {
317 return ftp_delete($this->getConnection(), $path);
318 }
319
320 /**
321 * @inheritdoc
322 */
323 public function deleteDir($dirname)
324 {
325 $connection = $this->getConnection();
326 $contents = array_reverse($this->listDirectoryContents($dirname, false));
327
328 foreach ($contents as $object) {
329 if ($object['type'] === 'file') {
330 if ( ! ftp_delete($connection, $object['path'])) {
331 return false;
332 }
333 } elseif ( ! $this->deleteDir($object['path'])) {
334 return false;
335 }
336 }
337
338 return ftp_rmdir($connection, $dirname);
339 }
340
341 /**
342 * @inheritdoc
343 */
344 public function createDir($dirname, Config $config)
345 {
346 $connection = $this->getConnection();
347 $directories = explode('/', $dirname);
348
349 foreach ($directories as $directory) {
350 if (false === $this->createActualDirectory($directory, $connection)) {
351 $this->setConnectionRoot();
352
353 return false;
354 }
355
356 ftp_chdir($connection, $directory);
357 }
358
359 $this->setConnectionRoot();
360
361 return ['type' => 'dir', 'path' => $dirname];
362 }
363
364 /**
365 * Create a directory.
366 *
367 * @param string $directory
368 * @param resource $connection
369 *
370 * @return bool
371 */
372 protected function createActualDirectory($directory, $connection)
373 {
374 // List the current directory
375 $listing = ftp_nlist($connection, '.') ?: [];
376
377 foreach ($listing as $key => $item) {
378 if (preg_match('~^\./.*~', $item)) {
379 $listing[$key] = substr($item, 2);
380 }
381 }
382
383 if (in_array($directory, $listing, true)) {
384 return true;
385 }
386
387 return (boolean) ftp_mkdir($connection, $directory);
388 }
389
390 /**
391 * @inheritdoc
392 */
393 public function getMetadata($path)
394 {
395 if ($path === '') {
396 return ['type' => 'dir', 'path' => ''];
397 }
398
399 if (@ftp_chdir($this->getConnection(), $path) === true) {
400 $this->setConnectionRoot();
401
402 return ['type' => 'dir', 'path' => $path];
403 }
404
405 $listing = $this->ftpRawlist('-A', $path);
406
407 if (empty($listing) || in_array('total 0', $listing, true)) {
408 return false;
409 }
410
411 if (preg_match('/.* not found/', $listing[0])) {
412 return false;
413 }
414
415 if (preg_match('/^total [0-9]*$/', $listing[0])) {
416 array_shift($listing);
417 }
418
419 return $this->normalizeObject($listing[0], '');
420 }
421
422 /**
423 * @inheritdoc
424 */
425 public function getMimetype($path)
426 {
427 if ( ! $metadata = $this->getMetadata($path)) {
428 return false;
429 }
430
431 $metadata['mimetype'] = MimeType::detectByFilename($path);
432
433 return $metadata;
434 }
435
436 /**
437 * @inheritdoc
438 */
439 public function getTimestamp($path)
440 {
441 $timestamp = ftp_mdtm($this->getConnection(), $path);
442
443 return ($timestamp !== -1) ? ['path' => $path, 'timestamp' => $timestamp] : false;
444 }
445
446 /**
447 * @inheritdoc
448 */
449 public function read($path)
450 {
451 if ( ! $object = $this->readStream($path)) {
452 return false;
453 }
454
455 $object['contents'] = stream_get_contents($object['stream']);
456 fclose($object['stream']);
457 unset($object['stream']);
458
459 return $object;
460 }
461
462 /**
463 * @inheritdoc
464 */
465 public function readStream($path)
466 {
467 $stream = fopen('php://temp', 'w+b');
468 $result = ftp_fget($this->getConnection(), $stream, $path, $this->transferMode);
469 rewind($stream);
470
471 if ( ! $result) {
472 fclose($stream);
473
474 return false;
475 }
476
477 return ['type' => 'file', 'path' => $path, 'stream' => $stream];
478 }
479
480 /**
481 * @inheritdoc
482 */
483 public function setVisibility($path, $visibility)
484 {
485 $mode = $visibility === AdapterInterface::VISIBILITY_PUBLIC ? $this->getPermPublic() : $this->getPermPrivate();
486
487 if ( ! ftp_chmod($this->getConnection(), $mode, $path)) {
488 return false;
489 }
490
491 return compact('path', 'visibility');
492 }
493
494 /**
495 * @inheritdoc
496 *
497 * @param string $directory
498 */
499 protected function listDirectoryContents($directory, $recursive = true)
500 {
501 if ($recursive && $this->recurseManually) {
502 return $this->listDirectoryContentsRecursive($directory);
503 }
504
505 $options = $recursive ? '-alnR' : '-aln';
506 $listing = $this->ftpRawlist($options, $directory);
507
508 return $listing ? $this->normalizeListing($listing, $directory) : [];
509 }
510
511 /**
512 * @inheritdoc
513 *
514 * @param string $directory
515 */
516 protected function listDirectoryContentsRecursive($directory)
517 {
518 $listing = $this->normalizeListing($this->ftpRawlist('-aln', $directory) ?: [], $directory);
519 $output = [];
520
521 foreach ($listing as $item) {
522 $output[] = $item;
523 if ($item['type'] !== 'dir') {
524 continue;
525 }
526 $output = array_merge($output, $this->listDirectoryContentsRecursive($item['path']));
527 }
528
529 return $output;
530 }
531
532 /**
533 * Check if the connection is open.
534 *
535 * @return bool
536 *
537 * @throws ConnectionErrorException
538 */
539 public function isConnected()
540 {
541 return $this->hasFtpConnection() && $this->getRawExecResponseCode('NOOP') === 200;
542 }
543
544 /**
545 * @return bool
546 */
547 protected function isPureFtpdServer()
548 {
549 $response = ftp_raw($this->connection, 'HELP');
550
551 return stripos(implode(' ', $response), 'Pure-FTPd') !== false;
552 }
553
554 /**
555 * The ftp_rawlist function with optional escaping.
556 *
557 * @param string $options
558 * @param string $path
559 *
560 * @return array
561 */
562 protected function ftpRawlist($options, $path)
563 {
564 $connection = $this->getConnection();
565
566 if ($this->isPureFtpd) {
567 $path = str_replace([' ', '[', ']'], ['\ ', '\\[', '\\]'], $path);
568 }
569
570 return ftp_rawlist($connection, $options . ' ' . $this->escapePath($path));
571 }
572
573 private function getRawExecResponseCode($command)
574 {
575 $response = @ftp_raw($this->connection, trim($command)) ?: [];
576
577 return (int) preg_replace('/\D/', '', implode(' ', (array) $response));
578 }
579
580 private function hasFtpConnection(): bool
581 {
582 return is_resource($this->connection) || $this->connection instanceof \FTP\Connection;
583 }
584 }
585