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