ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Ftp.php
Go to the documentation of this file.
1<?php
2
4
5use ErrorException;
11use RuntimeException;
12
14{
16
20 protected $transferMode = FTP_BINARY;
21
25 protected $ignorePassiveAddress = null;
26
30 protected $recurseManually = false;
31
35 protected $utf8 = false;
36
40 protected $configurable = [
41 'host',
42 'port',
43 'username',
44 'password',
45 'ssl',
46 'timeout',
47 'root',
48 'permPrivate',
49 'permPublic',
50 'passive',
51 'transferMode',
52 'systemType',
53 'ignorePassiveAddress',
54 'recurseManually',
55 'utf8',
56 ];
57
61 protected $isPureFtpd;
62
70 public function setTransferMode($mode)
71 {
72 $this->transferMode = $mode;
73
74 return $this;
75 }
76
84 public function setSsl($ssl)
85 {
86 $this->ssl = (bool) $ssl;
87
88 return $this;
89 }
90
96 public function setPassive($passive = true)
97 {
98 $this->passive = $passive;
99 }
100
105 {
106 $this->ignorePassiveAddress = $ignorePassiveAddress;
107 }
108
113 {
114 $this->recurseManually = $recurseManually;
115 }
116
120 public function setUtf8($utf8)
121 {
122 $this->utf8 = (bool) $utf8;
123 }
124
128 public function connect()
129 {
130 if ($this->ssl) {
131 $this->connection = ftp_ssl_connect($this->getHost(), $this->getPort(), $this->getTimeout());
132 } else {
133 $this->connection = ftp_connect($this->getHost(), $this->getPort(), $this->getTimeout());
134 }
135
136 if ( ! $this->connection) {
137 throw new RuntimeException('Could not connect to host: ' . $this->getHost() . ', port:' . $this->getPort());
138 }
139
140 $this->login();
141 $this->setUtf8Mode();
143 $this->setConnectionRoot();
144 $this->isPureFtpd = $this->isPureFtpdServer();
145 }
146
150 protected function setUtf8Mode()
151 {
152 if ($this->utf8) {
153 $response = ftp_raw($this->connection, "OPTS UTF8 ON");
154 if (substr($response[0], 0, 3) !== '200') {
155 throw new RuntimeException(
156 'Could not set UTF-8 mode for connection: ' . $this->getHost() . '::' . $this->getPort()
157 );
158 }
159 }
160 }
161
167 protected function setConnectionPassiveMode()
168 {
169 if (is_bool($this->ignorePassiveAddress) && defined('FTP_USEPASVADDRESS')) {
170 ftp_set_option($this->connection, FTP_USEPASVADDRESS, ! $this->ignorePassiveAddress);
171 }
172
173 if ( ! ftp_pasv($this->connection, $this->passive)) {
174 throw new RuntimeException(
175 'Could not set passive mode for connection: ' . $this->getHost() . '::' . $this->getPort()
176 );
177 }
178 }
179
183 protected function setConnectionRoot()
184 {
185 $root = $this->getRoot();
187
188 if (empty($root) === false && ! ftp_chdir($connection, $root)) {
189 throw new RuntimeException('Root is invalid or does not exist: ' . $this->getRoot());
190 }
191
192 // Store absolute path for further reference.
193 // This is needed when creating directories and
194 // initial root was a relative path, else the root
195 // would be relative to the chdir'd path.
196 $this->root = ftp_pwd($connection);
197 }
198
204 protected function login()
205 {
206 set_error_handler(function () {});
207 $isLoggedIn = ftp_login(
208 $this->connection,
209 $this->getUsername(),
210 $this->getPassword()
211 );
212 restore_error_handler();
213
214 if ( ! $isLoggedIn) {
215 $this->disconnect();
216 throw new RuntimeException(
217 'Could not login with connection: ' . $this->getHost() . '::' . $this->getPort(
218 ) . ', username: ' . $this->getUsername()
219 );
220 }
221 }
222
226 public function disconnect()
227 {
228 if (is_resource($this->connection)) {
229 ftp_close($this->connection);
230 }
231
232 $this->connection = null;
233 }
234
238 public function write($path, $contents, Config $config)
239 {
240 $stream = fopen('php://temp', 'w+b');
241 fwrite($stream, $contents);
242 rewind($stream);
243 $result = $this->writeStream($path, $stream, $config);
244 fclose($stream);
245
246 if ($result === false) {
247 return false;
248 }
249
250 $result['contents'] = $contents;
251 $result['mimetype'] = Util::guessMimeType($path, $contents);
252
253 return $result;
254 }
255
259 public function writeStream($path, $resource, Config $config)
260 {
262
263 if ( ! ftp_fput($this->getConnection(), $path, $resource, $this->transferMode)) {
264 return false;
265 }
266
267 if ($visibility = $config->get('visibility')) {
268 $this->setVisibility($path, $visibility);
269 }
270
271 $type = 'file';
272
273 return compact('type', 'path', 'visibility');
274 }
275
279 public function update($path, $contents, Config $config)
280 {
281 return $this->write($path, $contents, $config);
282 }
283
287 public function updateStream($path, $resource, Config $config)
288 {
289 return $this->writeStream($path, $resource, $config);
290 }
291
295 public function rename($path, $newpath)
296 {
297 return ftp_rename($this->getConnection(), $path, $newpath);
298 }
299
303 public function delete($path)
304 {
305 return ftp_delete($this->getConnection(), $path);
306 }
307
311 public function deleteDir($dirname)
312 {
313 $connection = $this->getConnection();
314 $contents = array_reverse($this->listDirectoryContents($dirname));
315
316 foreach ($contents as $object) {
317 if ($object['type'] === 'file') {
318 if ( ! ftp_delete($connection, $object['path'])) {
319 return false;
320 }
321 } elseif ( ! ftp_rmdir($connection, $object['path'])) {
322 return false;
323 }
324 }
325
326 return ftp_rmdir($connection, $dirname);
327 }
328
332 public function createDir($dirname, Config $config)
333 {
334 $connection = $this->getConnection();
335 $directories = explode('/', $dirname);
336
337 foreach ($directories as $directory) {
338 if (false === $this->createActualDirectory($directory, $connection)) {
339 $this->setConnectionRoot();
340
341 return false;
342 }
343
344 ftp_chdir($connection, $directory);
345 }
346
347 $this->setConnectionRoot();
348
349 return ['type' => 'dir', 'path' => $dirname];
350 }
351
360 protected function createActualDirectory($directory, $connection)
361 {
362 // List the current directory
363 $listing = ftp_nlist($connection, '.') ?: [];
364
365 foreach ($listing as $key => $item) {
366 if (preg_match('~^\./.*~', $item)) {
367 $listing[$key] = substr($item, 2);
368 }
369 }
370
371 if (in_array($directory, $listing, true)) {
372 return true;
373 }
374
375 return (boolean) ftp_mkdir($connection, $directory);
376 }
377
381 public function getMetadata($path)
382 {
383 $connection = $this->getConnection();
384
385 if ($path === '') {
386 return ['type' => 'dir', 'path' => ''];
387 }
388
389 if (@ftp_chdir($connection, $path) === true) {
390 $this->setConnectionRoot();
391
392 return ['type' => 'dir', 'path' => $path];
393 }
394
395 $listing = $this->ftpRawlist('-A', str_replace('*', '\\*', $path));
396
397 if (empty($listing) || in_array('total 0', $listing, true)) {
398 return false;
399 }
400
401 if (preg_match('/.* not found/', $listing[0])) {
402 return false;
403 }
404
405 if (preg_match('/^total [0-9]*$/', $listing[0])) {
406 array_shift($listing);
407 }
408
409 return $this->normalizeObject($listing[0], '');
410 }
411
415 public function getMimetype($path)
416 {
417 if ( ! $metadata = $this->getMetadata($path)) {
418 return false;
419 }
420
422
423 return $metadata;
424 }
425
429 public function getTimestamp($path)
430 {
431 $timestamp = ftp_mdtm($this->getConnection(), $path);
432
433 return ($timestamp !== -1) ? ['path' => $path, 'timestamp' => $timestamp] : false;
434 }
435
439 public function read($path)
440 {
441 if ( ! $object = $this->readStream($path)) {
442 return false;
443 }
444
445 $object['contents'] = stream_get_contents($object['stream']);
446 fclose($object['stream']);
447 unset($object['stream']);
448
449 return $object;
450 }
451
455 public function readStream($path)
456 {
457 $stream = fopen('php://temp', 'w+b');
458 $result = ftp_fget($this->getConnection(), $stream, $path, $this->transferMode);
459 rewind($stream);
460
461 if ( ! $result) {
462 fclose($stream);
463
464 return false;
465 }
466
467 return ['type' => 'file', 'path' => $path, 'stream' => $stream];
468 }
469
473 public function setVisibility($path, $visibility)
474 {
475 $mode = $visibility === AdapterInterface::VISIBILITY_PUBLIC ? $this->getPermPublic() : $this->getPermPrivate();
476
477 if ( ! ftp_chmod($this->getConnection(), $mode, $path)) {
478 return false;
479 }
480
481 return compact('path', 'visibility');
482 }
483
489 protected function listDirectoryContents($directory, $recursive = true)
490 {
491 $directory = str_replace('*', '\\*', $directory);
492
493 if ($recursive && $this->recurseManually) {
494 return $this->listDirectoryContentsRecursive($directory);
495 }
496
497 $options = $recursive ? '-alnR' : '-aln';
498 $listing = $this->ftpRawlist($options, $directory);
499
500 return $listing ? $this->normalizeListing($listing, $directory) : [];
501 }
502
508 protected function listDirectoryContentsRecursive($directory)
509 {
510 $listing = $this->normalizeListing($this->ftpRawlist('-aln', $directory) ?: []);
511 $output = [];
512
513 foreach ($listing as $directory) {
514 $output[] = $directory;
515 if ($directory['type'] !== 'dir') continue;
516
517 $output = array_merge($output, $this->listDirectoryContentsRecursive($directory['path']));
518 }
519
520 return $output;
521 }
522
529 public function isConnected()
530 {
531 try {
532 return is_resource($this->connection) && ftp_rawlist($this->connection, '/') !== false;
533 } catch (ErrorException $e) {
534 if (strpos($e->getMessage(), 'ftp_rawlist') === false) {
535 throw $e;
536 }
537
538 return false;
539 }
540 }
541
545 protected function isPureFtpdServer()
546 {
547 $response = ftp_raw($this->connection, 'HELP');
548
549 return stripos(implode(' ', $response), 'Pure-FTPd') !== false;
550 }
551
560 protected function ftpRawlist($options, $path)
561 {
562 $connection = $this->getConnection();
563
564 if ($this->isPureFtpd) {
565 $path = str_replace(' ', '\ ', $path);
566 }
567 return ftp_rawlist($connection, $options . ' ' . $path);
568 }
569}
$result
$metadata['__DYNAMIC:1__']
$path
Definition: aliased.php:25
foreach($mandatory_scripts as $file) $timestamp
Definition: buildRTE.php:81
An exception for terminatinating execution or to throw for unit testing.
normalizeListing(array $listing, $prefix='')
Normalize a directory listing.
getRoot()
Returns the root folder to work from.
ensureDirectory($dirname)
Ensure a directory exists.
getTimeout()
Returns the amount of seconds before the connection will timeout.
getPermPublic()
Get the public permission value.
getPermPrivate()
Get the private permission value.
normalizeObject($item, $base)
Normalize a file entry.
setIgnorePassiveAddress($ignorePassiveAddress)
Definition: Ftp.php:104
deleteDir($dirname)
Delete a directory.bool
Definition: Ftp.php:311
setUtf8Mode()
Set the connection to UTF-8 mode.
Definition: Ftp.php:150
listDirectoryContents($directory, $recursive=true)
Definition: Ftp.php:489
getMimetype($path)
Get the mimetype of a file.array|false
Definition: Ftp.php:415
read($path)
Read a file.array|false
Definition: Ftp.php:439
write($path, $contents, Config $config)
Write a new file.array|false false on failure file meta data on success
Definition: Ftp.php:238
setPassive($passive=true)
Set if passive mode should be used.
Definition: Ftp.php:96
getTimestamp($path)
Get the timestamp of a file.array|false
Definition: Ftp.php:429
createDir($dirname, Config $config)
Create a directory.array|false
Definition: Ftp.php:332
update($path, $contents, Config $config)
Update a file.array|false false on failure file meta data on success
Definition: Ftp.php:279
setVisibility($path, $visibility)
Set the visibility for a file.array|false file meta data
Definition: Ftp.php:473
listDirectoryContentsRecursive($directory)
Definition: Ftp.php:508
setConnectionRoot()
Set the connection root.
Definition: Ftp.php:183
setTransferMode($mode)
Set the transfer mode.
Definition: Ftp.php:70
disconnect()
Disconnect from the FTP server.
Definition: Ftp.php:226
setSsl($ssl)
Set if Ssl is enabled.
Definition: Ftp.php:84
setRecurseManually($recurseManually)
Definition: Ftp.php:112
createActualDirectory($directory, $connection)
Create a directory.
Definition: Ftp.php:360
updateStream($path, $resource, Config $config)
Update a file using a stream.array|false false on failure file meta data on success
Definition: Ftp.php:287
rename($path, $newpath)
Rename a file.bool
Definition: Ftp.php:295
ftpRawlist($options, $path)
The ftp_rawlist function with optional escaping.
Definition: Ftp.php:560
connect()
Connect to the FTP server.
Definition: Ftp.php:128
readStream($path)
Read a file as a stream.array|false
Definition: Ftp.php:455
isConnected()
Check if the connection is open.
Definition: Ftp.php:529
writeStream($path, $resource, Config $config)
Write a new file using a stream.array|false false on failure file meta data on success
Definition: Ftp.php:259
setConnectionPassiveMode()
Set the connections to passive mode.
Definition: Ftp.php:167
getMetadata($path)
Get all the meta data of a file or directory.array|false
Definition: Ftp.php:381
static detectByFilename($filename)
Definition: MimeType.php:61
static dirname($path)
Get a normalized dirname from a path.
Definition: Util.php:45
static guessMimeType($path, $content)
Guess MIME Type based on the path of the file and it's content.
Definition: Util.php:177
$key
Definition: croninfo.php:18
const VISIBILITY_PUBLIC
@const VISIBILITY_PUBLIC public visibility
$config
Definition: bootstrap.php:15
$stream
PHP stream implementation.
$type
$response