Overview

Packages

  • Vfs

Classes

  • Horde_Vfs
  • Horde_Vfs_Base
  • Horde_Vfs_Browser
  • Horde_Vfs_Exception
  • Horde_Vfs_File
  • Horde_Vfs_Ftp
  • Horde_Vfs_Gc
  • Horde_Vfs_Horde
  • Horde_Vfs_Kolab
  • Horde_Vfs_ListItem
  • Horde_Vfs_Musql
  • Horde_Vfs_Object
  • Horde_Vfs_Smb
  • Horde_Vfs_Sql
  • Horde_Vfs_SqlFile
  • Horde_Vfs_Ssh2
  • Horde_Vfs_Translation
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * VFS implementation for an FTP server.
  4:  *
  5:  * Required values for $params:<pre>
  6:  * username - (string) The username with which to connect to the ftp server.
  7:  * password - (string) The password with which to connect to the ftp server.
  8:  * hostspec - (string) The ftp server to connect to.</pre>
  9:  *
 10:  * Optional values for $params:<pre>
 11:  * lsformat - (string) The return formatting from the 'ls' command).
 12:  *                       Values: 'aix', 'standard' (default)
 13:  * maplocalids - (boolean) If true and the POSIX extension is available, the
 14:  *               driver will map the user and group IDs returned from the FTP
 15:  *               server with the local IDs from the local password file.  This
 16:  *               is useful only if the FTP server is running on localhost or
 17:  *               if the local user/group IDs are identical to the remote FTP
 18:  *               server.
 19:  * pasv - (boolean) If true, connection will be set to passive mode.
 20:  * port - (integer) The port used to connect to the ftp server if other than
 21:  *        21 (FTP default).
 22:  * ssl - (boolean) If true, and PHP had been compiled with OpenSSL support,
 23:  *        TLS transport-level encryption will be negotiated with the server.
 24:  * timeout -(integer) The timeout for the server.
 25:  * type - (string) The type of the remote FTP server.
 26:  *        Possible values: 'unix', 'win', 'netware'
 27:  *        By default, we attempt to auto-detect type.</pre>
 28:  *
 29:  * Copyright 2002-2012 Horde LLC (http://www.horde.org/)
 30:  * Copyright 2002-2007 Michael Varghese <mike.varghese@ascellatech.com>
 31:  *
 32:  * See the enclosed file COPYING for license information (LGPL). If you
 33:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 34:  *
 35:  * @author  Chuck Hagenbuch <chuck@horde.org>
 36:  * @author  Michael Varghese <mike.varghese@ascellatech.com>
 37:  * @package Vfs
 38:  */
 39: class Horde_Vfs_Ftp extends Horde_Vfs_Base
 40: {
 41:     /**
 42:      * Hash containing connection parameters.
 43:      *
 44:      * @var array
 45:      */
 46:     protected $_params = array('port' => 21);
 47: 
 48:     /**
 49:      * List of additional credentials required for this VFS backend.
 50:      *
 51:      * @var array
 52:      */
 53:     protected $_credentials = array('username', 'password');
 54: 
 55:     /**
 56:      * List of permissions and if they can be changed in this VFS backend.
 57:      *
 58:      * @var array
 59:      */
 60:     protected $_permissions = array(
 61:         'owner' => array(
 62:             'read' => true,
 63:             'write' => true,
 64:             'execute' => true
 65:         ),
 66:         'group' => array(
 67:             'read' => true,
 68:             'write' => true,
 69:             'execute' => true
 70:         ),
 71:         'all' => array(
 72:             'read' => true,
 73:             'write' => true,
 74:             'execute' => true
 75:         )
 76:     );
 77: 
 78:     /**
 79:      * Variable holding the connection to the ftp server.
 80:      *
 81:      * @var resource
 82:      */
 83:     protected $_stream = false;
 84: 
 85:     /**
 86:      * Local cache array for user IDs.
 87:      *
 88:      * @var array
 89:      */
 90:     protected $_uids = array();
 91: 
 92:     /**
 93:      * Local cache array for group IDs.
 94:      *
 95:      * @var array
 96:      */
 97:     protected $_gids = array();
 98: 
 99:     /**
100:      * The FTP server type.
101:      *
102:      * @var string
103:      */
104:     protected $_type;
105: 
106:     /**
107:      * Returns the size of a file.
108:      *
109:      * @param string $path  The path of the file.
110:      * @param string $name  The filename.
111:      *
112:      * @return integer  The size of the file in bytes.
113:      * @throws Horde_Vfs_Exception
114:      */
115:     public function size($path, $name)
116:     {
117:         $this->_connect();
118: 
119:         if (($size = @ftp_size($this->_stream, $this->_getPath($path, $name))) === -1) {
120:             throw new Horde_Vfs_Exception(sprintf('Unable to check file size of "%s".', $this->_getPath($path, $name)));
121:         }
122: 
123:         return $size;
124:     }
125: 
126:     /**
127:      * Retrieves a file from the VFS.
128:      *
129:      * @param string $path  The pathname to the file.
130:      * @param string $name  The filename to retrieve.
131:      *
132:      * @return string  The file data.
133:      */
134:     public function read($path, $name)
135:     {
136:         $file = $this->readFile($path, $name);
137:         clearstatcache();
138:         $size = filesize($file);
139: 
140:         return ($size === 0)
141:             ? ''
142:             : file_get_contents($file);
143:     }
144: 
145:     /**
146:      * Retrieves a file from the VFS as an on-disk local file.
147:      *
148:      * This function provides a file on local disk with the data of a VFS file
149:      * in it. This file <em>cannot</em> be modified! The behavior if you do
150:      * modify it is undefined. It will be removed at the end of the request.
151:      *
152:      * @param string $path  The pathname to the file.
153:      * @param string $name  The filename to retrieve.
154:      *
155:      * @return string  A local filename.
156:      * @throws Horde_Vfs_Exception
157:      */
158:     public function readFile($path, $name)
159:     {
160:         $this->_connect();
161: 
162:         // Create a temporary file and register it for deletion at the
163:         // end of this request.
164:         if (!($localFile = Horde_Util::getTempFile('vfs'))) {
165:             throw new Horde_Vfs_Exception('Unable to create temporary file.');
166:         }
167: 
168:         $result = @ftp_get(
169:             $this->_stream,
170:             $localFile,
171:             $this->_getPath($path, $name),
172:             FTP_BINARY);
173: 
174:         if ($result === false) {
175:             throw new Horde_Vfs_Exception(sprintf('Unable to open VFS file "%s".', $this->_getPath($path, $name)));
176:         }
177: 
178:         clearstatcache();
179: 
180:         return $localFile;
181:     }
182: 
183:     /**
184:      * Open a stream to a file in the VFS.
185:      *
186:      * @param string $path  The pathname to the file.
187:      * @param string $name  The filename to retrieve.
188:      *
189:      * @return resource  The stream.
190:      * @throws Horde_Vfs_Exception
191:      */
192:     public function readStream($path, $name)
193:     {
194:         return fopen($this->readFile($path, $name), OS_WINDOWS ? 'rb' : 'r');
195:     }
196: 
197:     /**
198:      * Stores a file in the VFS.
199:      *
200:      * @param string $path         The path to store the file in.
201:      * @param string $name         The filename to use.
202:      * @param string $tmpFile      The temporary file containing the data to
203:      *                             be stored.
204:      * @param boolean $autocreate  Automatically create directories?
205:      *
206:      * @throws Horde_Vfs_Exception
207:      */
208:     public function write($path, $name, $tmpFile, $autocreate = false)
209:     {
210:         $this->_connect();
211:         $this->_checkQuotaWrite('file', $tmpFile);
212: 
213:         if (!@ftp_put($this->_stream, $this->_getPath($path, $name), $tmpFile, FTP_BINARY)) {
214:             if ($autocreate) {
215:                 $this->autocreatePath($path);
216:                 if (@ftp_put($this->_stream, $this->_getPath($path, $name), $tmpFile, FTP_BINARY)) {
217:                     return;
218:                 }
219:             }
220: 
221:             throw new Horde_Vfs_Exception(sprintf('Unable to write VFS file "%s".', $this->_getPath($path, $name)));
222:         }
223:     }
224: 
225:     /**
226:      * Stores a file in the VFS from raw data.
227:      *
228:      * @param string $path         The path to store the file in.
229:      * @param string $name         The filename to use.
230:      * @param string $data         The file data.
231:      * @param boolean $autocreate  Automatically create directories?
232:      *
233:      * @throws Horde_Vfs_Exception
234:      */
235:     public function writeData($path, $name, $data, $autocreate = false)
236:     {
237:         $this->_checkQuotaWrite('string', $data);
238:         $tmpFile = Horde_Util::getTempFile('vfs');
239:         file_put_contents($tmpFile, $data);
240:         try {
241:             $this->write($path, $name, $tmpFile, $autocreate);
242:             unlink($tmpFile);
243:         } catch (Horde_Vfs_Exception $e) {
244:             unlink($tmpFile);
245:             throw $e;
246:         }
247:     }
248: 
249:     /**
250:      * Deletes a file from the VFS.
251:      *
252:      * @param string $path  The path to delete the file from.
253:      * @param string $name  The filename to delete.
254:      *
255:      * @throws Horde_Vfs_Exception
256:      */
257:     public function deleteFile($path, $name)
258:     {
259:         $this->_connect();
260:         $this->_checkQuotaDelete($path, $name);
261: 
262:         if (!@ftp_delete($this->_stream, $this->_getPath($path, $name))) {
263:             throw new Horde_Vfs_Exception(sprintf('Unable to delete VFS file "%s".', $this->_getPath($path, $name)));
264:         }
265:     }
266: 
267:     /**
268:      * Checks if a given item is a folder.
269:      *
270:      * @param string $path  The parent folder.
271:      * @param string $name  The item name.
272:      *
273:      * @return boolean  True if it is a folder, false otherwise.
274:      */
275:     public function isFolder($path, $name)
276:     {
277:         $result = false;
278: 
279:         try {
280:             $this->_connect();
281: 
282:             $olddir = $this->getCurrentDirectory();
283: 
284:             /* See if we can change to the given path. */
285:             $result = @ftp_chdir($this->_stream, $this->_getPath($path, $name));
286: 
287:             $this->_setPath($olddir);
288:         } catch (Horde_Vfs_Exception $e) {
289:         }
290: 
291:         return $result;
292:     }
293: 
294:     /**
295:      * Deletes a folder from the VFS.
296:      *
297:      * @param string $path        The parent folder.
298:      * @param string $name        The name of the folder to delete.
299:      * @param boolean $recursive  Force a recursive delete?
300:      *
301:      * @throws Horde_Vfs_Exception
302:      */
303:     public function deleteFolder($path, $name, $recursive = false)
304:     {
305:         $this->_connect();
306: 
307:         $isDir = false;
308:         foreach ($this->listFolder($path) as $file) {
309:             if ($file['name'] == $name && $file['type'] == '**dir') {
310:                 $isDir = true;
311:                 break;
312:             }
313:         }
314: 
315:         if ($isDir) {
316:             $dir = ltrim($path . '/' . $name, '/');
317:             $file_list = $this->listFolder($dir);
318:             if (count($file_list) && !$recursive) {
319:                 throw new Horde_Vfs_Exception(sprintf('Unable to delete "%s", as the directory is not empty.', $this->_getPath($path, $name)));
320:             }
321: 
322:             foreach ($file_list as $file) {
323:                 if ($file['type'] == '**dir') {
324:                     $this->deleteFolder($dir, $file['name'], $recursive);
325:                 } else {
326:                     $this->deleteFile($dir, $file['name']);
327:                 }
328:             }
329: 
330:             if (!@ftp_rmdir($this->_stream, $this->_getPath($path, $name))) {
331:                 throw new Horde_Vfs_Exception(sprintf('Cannot remove directory "%s".', $this->_getPath($path, $name)));
332:             }
333:         } elseif (!@ftp_delete($this->_stream, $this->_getPath($path, $name))) {
334:             throw new Horde_Vfs_Exception(sprintf('Cannot delete file "%s".', $this->_getPath($path, $name)));
335:         }
336:     }
337: 
338:     /**
339:      * Renames a file in the VFS.
340:      *
341:      * @param string $oldpath  The old path to the file.
342:      * @param string $oldname  The old filename.
343:      * @param string $newpath  The new path of the file.
344:      * @param string $newname  The new filename.
345:      *
346:      * @throws Horde_Vfs_Exception
347:      */
348:     public function rename($oldpath, $oldname, $newpath, $newname)
349:     {
350:         $this->_connect();
351:         $this->autocreatePath($newpath);
352: 
353:         if (!@ftp_rename($this->_stream, $this->_getPath($oldpath, $oldname), $this->_getPath($newpath, $newname))) {
354:             throw new Horde_Vfs_Exception(sprintf('Unable to rename VFS file "%s".', $this->_getPath($oldpath, $oldname)));
355:         }
356:     }
357: 
358:     /**
359:      * Creates a folder on the VFS.
360:      *
361:      * @param string $path  The parent folder.
362:      * @param string $name  The name of the new folder.
363:      *
364:      * @throws Horde_Vfs_Exception
365:      */
366:     public function createFolder($path, $name)
367:     {
368:         $this->_connect();
369: 
370:         if (!@ftp_mkdir($this->_stream, $this->_getPath($path, $name))) {
371:             throw new Horde_Vfs_Exception(sprintf('Unable to create VFS directory "%s".', $this->_getPath($path, $name)));
372:         }
373:     }
374: 
375:     /**
376:      * Changes permissions for an item on the VFS.
377:      *
378:      * @param string $path        The parent folder of the item.
379:      * @param string $name        The name of the item.
380:      * @param string $permission  The permission to set in octal notation.
381:      *
382:      * @throws Horde_Vfs_Exception
383:      */
384:     public function changePermissions($path, $name, $permission)
385:     {
386:         $this->_connect();
387: 
388:         if (!@ftp_site($this->_stream, 'CHMOD ' . $permission . ' ' . $this->_getPath($path, $name))) {
389:             throw new Horde_Vfs_Exception(sprintf('Unable to change permission for VFS file "%s".', $this->_getPath($path, $name)));
390:         }
391:     }
392: 
393:     /**
394:      * Returns an an unsorted file list of the specified directory.
395:      *
396:      * @param string $path       The path of the directory.
397:      * @param mixed $filter      String/hash to filter file/dirname on.
398:      * @param boolean $dotfiles  Show dotfiles?
399:      * @param boolean $dironly   Show only directories?
400:      *
401:      * @return array  File list.
402:      * @throws Horde_Vfs_Exception
403:      */
404:     protected function _listFolder($path = '', $filter = null,
405:                                    $dotfiles = true, $dironly = false)
406:     {
407:         $this->_connect();
408: 
409:         if (empty($this->_type)) {
410:             if (!empty($this->_params['type'])) {
411:                 $this->_type = $this->_params['type'];
412:             } else {
413:                 $type = Horde_String::lower(@ftp_systype($this->_stream));
414:                 if ($type == 'unknown') {
415:                     // Go with unix-style listings by default.
416:                     $type = 'unix';
417:                 } elseif (strpos($type, 'win') !== false) {
418:                     $type = 'win';
419:                 } elseif (strpos($type, 'netware') !== false) {
420:                     $type = 'netware';
421:                 }
422: 
423:                 $this->_type = $type;
424:             }
425:         }
426: 
427:         $olddir = $this->getCurrentDirectory();
428:         $path = $this->_getPath('', $path);
429:         if (strlen($path)) {
430:             $this->_setPath($path);
431:         }
432: 
433:         if ($this->_type == 'unix') {
434:             // If we don't want dotfiles, We can save work here by not
435:             // doing an ls -a and then not doing the check later (by
436:             // setting $dotfiles to true, the if is short-circuited).
437:             if ($dotfiles) {
438:                 $list = ftp_rawlist($this->_stream, '-al');
439:                 $dotfiles = true;
440:             } else {
441:                 $list = ftp_rawlist($this->_stream, '-l');
442:             }
443:         } else {
444:             $list = ftp_rawlist($this->_stream, '');
445:         }
446: 
447:         if (!is_array($list)) {
448:             if (isset($olddir)) {
449:                 $this->_setPath($olddir);
450:             }
451:             return array();
452:         }
453: 
454:         /* If 'maplocalids' is set, check for the POSIX extension. */
455:         $mapids = (!empty($this->_params['maplocalids']) && extension_loaded('posix'));
456: 
457:         $currtime = time();
458:         $files = array();
459: 
460:         foreach ($list as $line) {
461:             $file = array();
462: 
463:             $item = preg_split('/\s+/', $line);
464:             if (($this->_type == 'unix') ||
465:                 (($this->_type == 'win') && !preg_match('|\d\d-\d\d-\d\d|', $item[0]))) {
466:                 if (count($item) < 8 || substr($line, 0, 5) == 'total') {
467:                     continue;
468:                 }
469:                 $file['perms'] = $item[0];
470:                 if ($mapids) {
471:                     if (!isset($this->_uids[$item[2]])) {
472:                         $entry = posix_getpwuid($item[2]);
473:                         $this->_uids[$item[2]] = (empty($entry)) ? $item[2] : $entry['name'];
474:                     }
475:                     $file['owner'] = $this->_uids[$item[2]];
476:                     if (!isset($this->_uids[$item[3]])) {
477:                         $entry = posix_getgrgid($item[3]);
478:                         $this->_uids[$item[3]] = (empty($entry)) ? $item[3] : $entry['name'];
479:                     }
480:                     $file['group'] = $this->_uids[$item[3]];
481: 
482:                 } else {
483:                     $file['owner'] = $item[2];
484:                     $file['group'] = $item[3];
485:                 }
486: 
487:                 if (!empty($this->_params['lsformat']) &&
488:                     ($this->_params['lsformat'] == 'aix')) {
489:                     $file['name'] = substr($line, strpos($line, sprintf("%s %2s %-5s", $item[5], $item[6], $item[7])) + 13);
490:                 } else {
491:                     $file['name'] = substr($line, strpos($line, sprintf("%s %2s %5s", $item[5], $item[6], $item[7])) + 13);
492:                 }
493: 
494:                 // Filter out '.' and '..' entries.
495:                 if (preg_match('/^\.\.?\/?$/', $file['name'])) {
496:                     continue;
497:                 }
498: 
499:                 // Filter out dotfiles if they aren't wanted.
500:                 if (!$dotfiles && substr($file['name'], 0, 1) == '.') {
501:                     continue;
502:                 }
503: 
504:                 $p1 = substr($file['perms'], 0, 1);
505:                 if ($p1 === 'l') {
506:                     $file['link'] = substr($file['name'], strpos($file['name'], '->') + 3);
507:                     $file['name'] = substr($file['name'], 0, strpos($file['name'], '->') - 1);
508:                     $file['type'] = '**sym';
509: 
510:                     if ($this->isFolder('', $file['link'])) {
511:                         $file['linktype'] = '**dir';
512:                     } else {
513:                         $parts = explode('/', $file['link']);
514:                         $name = explode('.', array_pop($parts));
515:                         if (count($name) == 1 || ($name[0] === '' && count($name) == 2)) {
516:                             $file['linktype'] = '**none';
517:                         } else {
518:                             $file['linktype'] = Horde_String::lower(array_pop($name));
519:                         }
520:                     }
521:                 } elseif ($p1 === 'd') {
522:                     $file['type'] = '**dir';
523:                 } else {
524:                     $name = explode('.', $file['name']);
525:                     if (count($name) == 1 || (substr($file['name'], 0, 1) === '.' && count($name) == 2)) {
526:                         $file['type'] = '**none';
527:                     } else {
528:                         $file['type'] = Horde_String::lower($name[count($name) - 1]);
529:                     }
530:                 }
531:                 if ($file['type'] == '**dir') {
532:                     $file['size'] = -1;
533:                 } else {
534:                     $file['size'] = $item[4];
535:                 }
536:                 if (strpos($item[7], ':') !== false) {
537:                     $file['date'] = strtotime($item[7] . ':00' . $item[5] . ' ' . $item[6] . ' ' . date('Y', $currtime));
538:                     // If the ftp server reports a file modification date more
539:                     // less than one day in the future, don't try to subtract
540:                     // a year from the date.  There is no way to know, for
541:                     // example, if the VFS server and the ftp server reside
542:                     // in different timezones.  We should simply report to the
543:                     //  user what the FTP server is returning.
544:                     if ($file['date'] > ($currtime + 86400)) {
545:                         $file['date'] = strtotime($item[7] . ':00' . $item[5] . ' ' . $item[6] . ' ' . (date('Y', $currtime) - 1));
546:                     }
547:                 } else {
548:                     $file['date'] = strtotime('00:00:00' . $item[5] . ' ' . $item[6] . ' ' . $item[7]);
549:                 }
550:             } elseif ($this->_type == 'netware') {
551:                 if (count($item) < 8 || substr($line, 0, 5) == 'total') {
552:                     continue;
553:                 }
554: 
555:                 $file = array();
556:                 $file['perms'] = $item[1];
557:                 $file['owner'] = $item[2];
558:                 if ($item[0] == 'd') {
559:                     $file['type'] = '**dir';
560:                 } else {
561:                     $file['type'] = '**none';
562:                 }
563:                 $file['size'] = $item[3];
564: 
565:                 // We don't know the timezone here. Just report what the FTP server says.
566:                 if (strpos($item[6], ':') !== false) {
567:                     $file['date'] = strtotime($item[6] . ':00 ' . $item[5] . ' ' . $item[4] . ' ' . date('Y'));
568:                 } else {
569:                     $file['date'] = strtotime('00:00:00 ' . $item[5] . ' ' . $item[4] . ' ' . $item[6]);
570:                 }
571: 
572:                 $file['name'] = $item[7];
573:                 for ($index = 8, $c = count($item); $index < $c; $index++) {
574:                     $file['name'] .= ' ' . $item[$index];
575:                 }
576:             } else {
577:                 /* Handle Windows FTP servers returning DOS-style file
578:                  * listings. */
579:                 $file['perms'] = '';
580:                 $file['owner'] = '';
581:                 $file['group'] = '';
582:                 $file['name'] = $item[3];
583:                 for ($index = 4, $c = count($item); $index < $c; $index++) {
584:                     $file['name'] .= ' ' . $item[$index];
585:                 }
586:                 $file['date'] = strtotime($item[0] . ' ' . $item[1]);
587:                 if ($item[2] == '<DIR>') {
588:                     $file['type'] = '**dir';
589:                     $file['size'] = -1;
590:                 } else {
591:                     $file['size'] = $item[2];
592:                     $name = explode('.', $file['name']);
593:                     if (count($name) == 1 || (substr($file['name'], 0, 1) === '.' && count($name) == 2)) {
594:                         $file['type'] = '**none';
595:                     } else {
596:                         $file['type'] = Horde_String::lower($name[count($name) - 1]);
597:                     }
598:                 }
599:             }
600: 
601:             // Filtering.
602:             if ($this->_filterMatch($filter, $file['name'])) {
603:                 unset($file);
604:                 continue;
605:             }
606:             if ($dironly && $file['type'] !== '**dir') {
607:                 unset($file);
608:                 continue;
609:             }
610: 
611:             $files[$file['name']] = $file;
612:             unset($file);
613:         }
614: 
615:         if (isset($olddir)) {
616:             $this->_setPath($olddir);
617:         }
618: 
619:         return $files;
620:     }
621: 
622:     /**
623:      * Returns a sorted list of folders in the specified directory.
624:      *
625:      * @param string $path         The path of the directory to get the
626:      *                             directory list for.
627:      * @param mixed $filter        Hash of items to filter based on folderlist.
628:      * @param boolean $dotfolders  Include dotfolders?
629:      *
630:      * @return array  Folder list.
631:      * @throws Horde_Vfs_Exception
632:      */
633:     public function listFolders($path = '', $filter = null, $dotfolders = true)
634:     {
635:         $this->_connect();
636: 
637:         $folder = array(
638:             'abbrev' => '..',
639:             'label' => '..',
640:             'val' => $this->_parentDir($path)
641:         );
642:         $folders = array($folder['val'] => $folder);
643: 
644:         $folderList = $this->listFolder($path, null, $dotfolders, true);
645:         foreach ($folderList as $files) {
646:             $folders[$folder['val']] = array(
647:                 'abbrev' => $files['name'],
648:                 'label' => $folder['val'],
649:                 'val' => $this->_getPath($path, $files['name'])
650:             );
651:         }
652: 
653:         ksort($folders);
654: 
655:         return $folders;
656:     }
657: 
658:     /**
659:      * Copies a file through the backend.
660:      *
661:      * @param string $path         The path of the original file.
662:      * @param string $name         The name of the original file.
663:      * @param string $dest         The name of the destination directory.
664:      * @param boolean $autocreate  Automatically create directories?
665:      *
666:      * @throws Horde_Vfs_Exception
667:      */
668:     public function copy($path, $name, $dest, $autocreate = false)
669:     {
670:         $orig = $this->_getPath($path, $name);
671:         if (preg_match('|^' . preg_quote($orig) . '/?$|', $dest)) {
672:             throw new Horde_Vfs_Exception('Cannot copy file(s) - source and destination are the same.');
673:         }
674: 
675:         $this->_connect();
676: 
677:         if ($autocreate) {
678:             $this->autocreatePath($dest);
679:         }
680: 
681:         foreach ($this->listFolder($dest, null, true) as $file) {
682:             if ($file['name'] == $name) {
683:                 throw new Horde_Vfs_Exception(sprintf('%s already exists.'), $this->_getPath($dest, $name));
684:             }
685:         }
686: 
687:         if ($this->isFolder($path, $name)) {
688:             $this->_copyRecursive($path, $name, $dest);
689:         } else {
690:             $tmpFile = Horde_Util::getTempFile('vfs');
691:             $fetch = @ftp_get($this->_stream, $tmpFile, $orig, FTP_BINARY);
692:             if (!$fetch) {
693:                 unlink($tmpFile);
694:                 throw new Horde_Vfs_Exception(sprintf('Failed to copy from "%s".', $orig));
695:             }
696: 
697:             clearstatcache();
698:             $this->_checkQuotaWrite('file', $tmpFile);
699: 
700:             if (!@ftp_put($this->_stream, $this->_getPath($dest, $name), $tmpFile, FTP_BINARY)) {
701:                 unlink($tmpFile);
702:                 throw new Horde_Vfs_Exception(sprintf('Failed to copy to "%s".', $this->_getPath($dest, $name)));
703:             }
704: 
705:             unlink($tmpFile);
706:         }
707:     }
708: 
709:     /**
710:      * Moves a file through the backend.
711:      *
712:      * @param string $path         The path of the original file.
713:      * @param string $name         The name of the original file.
714:      * @param string $dest         The destination file name.
715:      * @param boolean $autocreate  Automatically create directories?
716:      *
717:      * @throws Horde_Vfs_Exception
718:      */
719:     public function move($path, $name, $dest, $autocreate = false)
720:     {
721:         $orig = $this->_getPath($path, $name);
722:         if (preg_match('|^' . preg_quote($orig) . '/?$|', $dest)) {
723:             throw new Horde_Vfs_Exception('Cannot move file(s) - destination is within source.');
724:         }
725: 
726:         $this->_connect();
727: 
728:         if ($autocreate) {
729:             $this->autocreatePath($dest);
730:         }
731: 
732:         foreach ($this->listFolder($dest, null, true) as $file) {
733:             if ($file['name'] == $name) {
734:                 throw new Horde_Vfs_Exception(sprintf('%s already exists.', $this->_getPath($dest, $name)));
735:             }
736:         }
737: 
738:         if (!@ftp_rename($this->_stream, $orig, $this->_getPath($dest, $name))) {
739:             throw new Horde_Vfs_Exception(sprintf('Failed to move to "%s".', $this->_getPath($dest, $name)));
740:         }
741:     }
742: 
743:     /**
744:      * Returns the current working directory on the FTP server.
745:      *
746:      * @return string  The current working directory.
747:      * @throws Horde_Vfs_Exception
748:      */
749:     public function getCurrentDirectory()
750:     {
751:         $this->_connect();
752:         return @ftp_pwd($this->_stream);
753:     }
754: 
755:     /**
756:      * Returns the full path of an item.
757:      *
758:      * @param string $path  The path of directory of the item.
759:      * @param string $name  The name of the item.
760:      *
761:      * @return mixed  Full path when $path isset and just $name when not set.
762:      */
763:     protected function _getPath($path, $name)
764:     {
765:         if (strlen($this->_params['vfsroot'])) {
766:             if (strlen($path)) {
767:                 $path = $this->_params['vfsroot'] . '/' . $path;
768:             } else {
769:                 $path = $this->_params['vfsroot'];
770:             }
771:         }
772:         return parent::_getPath($path, $name);
773:     }
774: 
775:     /**
776:      * Changes the current directory on the server.
777:      *
778:      * @param string $path  The path to change to.
779:      *
780:      * @throws Horde_Vfs_Exception
781:      */
782:     protected function _setPath($path)
783:     {
784:         if (!@ftp_chdir($this->_stream, $path)) {
785:             throw new Horde_Vfs_Exception(sprintf('Unable to change to %s.', $path));
786:         }
787:     }
788: 
789:     /**
790:      * Returns the parent directory of the specified path.
791:      *
792:      * @param string $path  The path to get the parent of.
793:      *
794:      * @return string  The parent directory.
795:      * @throws Horde_Vfs_Exception
796:      */
797:     protected function _parentDir($path)
798:     {
799:         $this->_connect();
800: 
801:         $olddir = $this->getCurrentDirectory();
802:         @ftp_cdup($this->_stream);
803: 
804:         $parent = $this->getCurrentDirectory();
805:         $this->_setPath($olddir);
806: 
807:         if (!$parent) {
808:             throw new Horde_Vfs_Exception('Unable to determine current directory.');
809:         }
810: 
811:         return $parent;
812:     }
813: 
814:     /**
815:      * Attempts to open a connection to the FTP server.
816:      *
817:      * @throws Horde_Vfs_Exception
818:      */
819:     protected function _connect()
820:     {
821:         if ($this->_stream !== false) {
822:             return;
823:         }
824: 
825:         if (!extension_loaded('ftp')) {
826:             throw new Horde_Vfs_Exception('The FTP extension is not available.');
827:         }
828: 
829:         if (!is_array($this->_params)) {
830:             throw new Horde_Vfs_Exception('No configuration information specified for FTP VFS.');
831:         }
832: 
833:         $required = array('hostspec', 'username', 'password');
834:         foreach ($required as $val) {
835:             if (!isset($this->_params[$val])) {
836:                 throw new Horde_Vfs_Exception(sprintf('Required "%s" not specified in VFS configuration.', $val));
837:             }
838:         }
839: 
840:         /* Connect to the ftp server using the supplied parameters. */
841:         if (!empty($this->_params['ssl'])) {
842:             if (function_exists('ftp_ssl_connect')) {
843:                 $this->_stream = @ftp_ssl_connect($this->_params['hostspec'], $this->_params['port']);
844:             } else {
845:                 throw new Horde_Vfs_Exception('Unable to connect with SSL.');
846:             }
847:         } else {
848:             $this->_stream = @ftp_connect($this->_params['hostspec'], $this->_params['port']);
849:         }
850: 
851:         if (!$this->_stream) {
852:             throw new Horde_Vfs_Exception('Connection to FTP server failed.');
853:         }
854: 
855:         if (!@ftp_login($this->_stream, $this->_params['username'], $this->_params['password'])) {
856:             @ftp_quit($this->_stream);
857:             $this->_stream = false;
858:             throw new Horde_Vfs_Exception('Authentication to FTP server failed.');
859:         }
860: 
861:         if (!empty($this->_params['pasv'])) {
862:             @ftp_pasv($this->_stream, true);
863:         }
864: 
865:         if (!empty($this->_params['timeout'])) {
866:             ftp_set_option($this->_stream, FTP_TIMEOUT_SEC, $this->_params['timeout']);
867:         }
868: 
869:         if (!empty($this->_params['vfsroot']) &&
870:             !@ftp_chdir($this->_stream, $this->_params['vfsroot']) &&
871:             !@ftp_mkdir($this->_stream, $this->_params['vfsroot'])) {
872:             throw new Horde_Vfs_Exception(sprintf('Unable to create VFS root directory "%s".', $this->_params['vfsroot']));
873:         }
874:     }
875: }
876: 
API documentation generated by ApiGen