ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Ftp.php
Go to the documentation of this file.
1 <?php
2 
4 
12 
13 class Ftp extends AbstractFtpAdapter
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();
142  $this->setConnectionPassiveMode();
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 }
connect()
Connect to the FTP server.
Definition: Ftp.php:128
const VISIBILITY_PUBLIC
VISIBILITY_PUBLIC public visibility
$path
Definition: aliased.php:25
static detectByFilename($filename)
Definition: MimeType.php:61
ensureDirectory($dirname)
Ensure a directory exists.
isConnected()
Check if the connection is open.
Definition: Ftp.php:529
setTransferMode($mode)
Set the transfer mode.
Definition: Ftp.php:70
static dirname($path)
Get a normalized dirname from a path.
Definition: Util.php:45
$config
Definition: bootstrap.php:15
$result
updateStream($path, $resource, Config $config)
Update a file using a stream.Config objectarray|false false on failure file meta data on success ...
Definition: Ftp.php:287
read($path)
Read a file.array|false
Definition: Ftp.php:439
setSsl($ssl)
Set if Ssl is enabled.
Definition: Ftp.php:84
$type
static guessMimeType($path, $content)
Guess MIME Type based on the path of the file and it&#39;s content.
Definition: Util.php:177
listDirectoryContentsRecursive($directory)
Definition: Ftp.php:508
setRecurseManually($recurseManually)
Definition: Ftp.php:112
setUtf8Mode()
Set the connection to UTF-8 mode.
Definition: Ftp.php:150
update($path, $contents, Config $config)
Update a file.Config objectarray|false false on failure file meta data on success ...
Definition: Ftp.php:279
getTimestamp($path)
Get the timestamp of a file.array|false
Definition: Ftp.php:429
normalizeObject($item, $base)
Normalize a file entry.
$metadata['__DYNAMIC:1__']
deleteDir($dirname)
Delete a directory.bool
Definition: Ftp.php:311
$stream
PHP stream implementation.
setPassive($passive=true)
Set if passive mode should be used.
Definition: Ftp.php:96
getMimetype($path)
Get the mimetype of a file.array|false
Definition: Ftp.php:415
createDir($dirname, Config $config)
Create a directory.directory name array|false
Definition: Ftp.php:332
getRoot()
Returns the root folder to work from.
setVisibility($path, $visibility)
Set the visibility for a file.array|false file meta data
Definition: Ftp.php:473
setIgnorePassiveAddress($ignorePassiveAddress)
Definition: Ftp.php:104
ftpRawlist($options, $path)
The ftp_rawlist function with optional escaping.
Definition: Ftp.php:560
getTimeout()
Returns the amount of seconds before the connection will timeout.
createActualDirectory($directory, $connection)
Create a directory.
Definition: Ftp.php:360
normalizeListing(array $listing, $prefix='')
Normalize a directory listing.
getMetadata($path)
Get all the meta data of a file or directory.array|false
Definition: Ftp.php:381
foreach($mandatory_scripts as $file) $timestamp
Definition: buildRTE.php:81
rename($path, $newpath)
Rename a file.bool
Definition: Ftp.php:295
writeStream($path, $resource, Config $config)
Write a new file using a stream.Config objectarray|false false on failure file meta data on success ...
Definition: Ftp.php:259
listDirectoryContents($directory, $recursive=true)
Definition: Ftp.php:489
write($path, $contents, Config $config)
Write a new file.Config objectarray|false false on failure file meta data on success ...
Definition: Ftp.php:238
readStream($path)
Read a file as a stream.array|false
Definition: Ftp.php:455
setConnectionPassiveMode()
Set the connections to passive mode.
Definition: Ftp.php:167
$response
getPermPrivate()
Get the private permission value.
$key
Definition: croninfo.php:18
getPermPublic()
Get the public permission value.
setConnectionRoot()
Set the connection root.
Definition: Ftp.php:183
get($key, $default=null)
Get a setting.
Definition: Config.php:35
disconnect()
Disconnect from the FTP server.
Definition: Ftp.php:226