Source for file ssh2.php
Documentation is available at ssh2.php
* VFS implementation for an SSH2 server.
* This module requires the SSH2 (version 0.10+) PECL package.
* Required values for $params:<pre>
* 'username' The username with which to connect to the ssh2 server.
* 'password' The password with which to connect to the ssh2 server.
* 'hostspec' The ssh2 server to connect to.</pre>
* Optional values for $params:<pre>
* 'port' The port used to connect to the ssh2 server if other
* $Horde: framework/VFS/lib/VFS/ssh2.php,v 1.1.2.15 2009/10/15 17:18:48 jan Exp $
* Copyright 2006-2009 The Horde Project (http://www.horde.org/)
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* @editor Cliff Green <green@umdnj.edu>
* List of additional credentials required for this VFS backend.
var $_credentials = array ('username', 'password');
* List of permissions and if they can be changed in this VFS backend.
var $_permissions = array (
'owner' => array ('read' => true , 'write' => true , 'execute' => true ),
'group' => array ('read' => true , 'write' => true , 'execute' => true ),
'all' => array ('read' => true , 'write' => true , 'execute' => true ));
* Variable holding the connection to the ssh2 server.
* The SFTP resource stream.
* The current working directory.
* Local cache array for user IDs.
* Local cache array for group IDs.
* Returns the size of a file.
* @param string $path The path of the file.
* @param string $name The filename.
* @return integer The size of the file in bytes or PEAR_Error on
function size($path, $name)
$conn = $this->_connect ();
if (is_a($conn, 'PEAR_Error')) {
$statinfo = @ssh2_sftp_stat ($this->_sftp, $this->_getPath($path, $name));
if (($size = $statinfo['size']) === false ) {
return PEAR ::raiseError (sprintf(_("Unable to check file size of \"%s\"."), $this->_getPath($path, $name)));
* Retrieves a file from the VFS.
* @param string $path The pathname to the file.
* @param string $name The filename to retrieve.
* @return string The file data.
function read($path, $name)
if (is_a($file, 'PEAR_Error')) {
* Retrieves a file from the VFS as an on-disk local file.
* This function provides a file on local disk with the data of a VFS file
* in it. This file <em>cannot</em> be modified! The behavior if you do
* modify it is undefined. It will be removed at the end of the request.
* @param string $path The pathname to the file.
* @param string $name The filename to retrieve.
* @return string A local filename.
$result = $this->_connect ();
if (is_a($result, 'PEAR_Error')) {
// Create a temporary file and register it for deletion at the
return PEAR ::raiseError (_("Unable to create temporary file."));
if (!$this->_recv ($this->_getPath($path, $name), $localFile)) {
return PEAR ::raiseError (sprintf(_("Unable to open VFS file \"%s\"."), $this->_getPath($path, $name)));
* Open a stream to a file in the VFS.
* @param string $path The pathname to the file.
* @param string $name The filename to retrieve.
* @return resource The stream.
if (is_a($file, 'PEAR_Error')) {
$mode = OS_WINDOWS ? 'rb' : 'r';
return fopen($file, $mode);
* Stores a file in the VFS.
* @param string $path The path to store the file in.
* @param string $name The filename to use.
* @param string $tmpFile The temporary file containing the data to
* @param boolean $autocreate Automatically create directories?
* @return mixed True on success or a PEAR_Error object on failure.
function write($path, $name, $tmpFile, $autocreate = false )
$conn = $this->_connect ();
if (is_a($conn, 'PEAR_Error')) {
$res = $this->_checkQuotaWrite ('file', $tmpFile);
if (is_a($res, 'PEAR_Error')) {
if (!$this->_send ($tmpFile, $this->_getPath($path, $name))) {
if (is_a($result, 'PEAR_Error')) {
if (!$this->_send ($tmpFile, $this->_getPath($path, $name))) {
return PEAR ::raiseError (sprintf(_("Unable to write VFS file \"%s\"."), $this->_getPath($path, $name)));
return PEAR ::raiseError (sprintf(_("Unable to write VFS file \"%s\"."), $this->_getPath($path, $name)));
* Stores a file in the VFS from raw data.
* @param string $path The path to store the file in.
* @param string $name The filename to use.
* @param string $data The file data.
* @param boolean $autocreate Automatically create directories?
* @return mixed True on success or a PEAR_Error object on failure.
function writeData($path, $name, $data, $autocreate = false )
$res = $this->_checkQuotaWrite ('string', $data);
if (is_a($res, 'PEAR_Error')) {
$fp = fopen($tmpFile, 'wb');
$result = $this->write($path, $name, $tmpFile, $autocreate);
* Deletes a file from the VFS.
* @param string $path The path to delete the file from.
* @param string $name The filename to delete.
* @return mixed True on success or a PEAR_Error object on failure.
$res = $this->_checkQuotaDelete ($path, $name);
if (is_a($res, 'PEAR_Error')) {
$conn = $this->_connect ();
if (is_a($conn, 'PEAR_Error')) {
if (!@ssh2_sftp_unlink ($this->_sftp, $this->_getPath($path, $name))) {
return PEAR ::raiseError (sprintf(_("Unable to delete VFS file \"%s\"."), $this->_getPath($path, $name)));
* Checks if a given item is a folder.
* @param string $path The parent folder.
* @param string $name The item name.
* @return boolean True if it is a folder, false otherwise.
$conn = $this->_connect ();
if (is_a($conn, 'PEAR_Error')) {
/* See if we can stat the remote filename. ANDed with 040000 is true
* if it is a directory. */
$statinfo = @ssh2_sftp_stat ($this->_sftp, $this->_getPath($path, $name));
return $statinfo['mode'] & 040000;
* Deletes a folder from the VFS.
* @param string $path The parent folder.
* @param string $name The name of the folder to delete.
* @param boolean $recursive Force a recursive delete?
* @return mixed True on success or a PEAR_Error object on failure.
$conn = $this->_connect ();
if (is_a($conn, 'PEAR_Error')) {
foreach ($dirCheck as $file) {
if ($file['name'] == $name && $file['type'] == '**dir') {
if (is_a($file_list, 'PEAR_Error')) {
if (count($file_list) && !$recursive) {
return PEAR ::raiseError (sprintf(_("Unable to delete \"%s\", the directory is not empty."),
foreach ($file_list as $file) {
if ($file['type'] == '**dir') {
if (is_a($result, 'PEAR_Error')) {
if (!@ssh2_sftp_rmdir ($this->_sftp, $this->_getPath($path, $name))) {
return PEAR ::raiseError (sprintf(_("Cannot remove directory \"%s\"."), $this->_getPath($path, $name)));
if (!@ssh2_sftp_unlink ($this->_sftp, $this->_getPath($path, $name))) {
return PEAR ::raiseError (sprintf(_("Cannot delete file \"%s\"."), $this->_getPath($path, $name)));
* Renames a file in the VFS.
* @param string $oldpath The old path to the file.
* @param string $oldname The old filename.
* @param string $newpath The new path of the file.
* @param string $newname The new filename.
* @return mixed True on success or a PEAR_Error object on failure.
function rename($oldpath, $oldname, $newpath, $newname)
$conn = $this->_connect ();
if (is_a($conn, 'PEAR_Error')) {
if (!@ssh2_sftp_rename ($this->_sftp, $this->_getPath($oldpath, $oldname), $this->_getPath($newpath, $newname))) {
return PEAR ::raiseError (sprintf(_("Unable to rename VFS file \"%s\"."), $this->_getPath($oldpath, $oldname)));
* Creates a folder on the VFS.
* @param string $path The parent folder.
* @param string $name The name of the new folder.
* @return mixed True on success or a PEAR_Error object on failure.
$conn = $this->_connect ();
if (is_a($conn, 'PEAR_Error')) {
if (!@ssh2_sftp_mkdir ($this->_sftp, $this->_getPath($path, $name))) {
return PEAR ::raiseError (sprintf(_("Unable to create VFS directory \"%s\"."), $this->_getPath($path, $name)));
* Changes permissions for an item on the VFS.
* @param string $path The parent folder of the item.
* @param string $name The name of the item.
* @param string $permission The permission to set.
* @return mixed True on success or a PEAR_Error object on failure.
$conn = $this->_connect ();
if (is_a($conn, 'PEAR_Error')) {
return PEAR ::raiseError (sprintf(_("Unable to change permission for VFS file \"%s\"."), $this->_getPath($path, $name)));
* Returns an an unsorted file list of the specified directory.
* @param string $path The path of the directory.
* @param mixed $filter String/hash to filter file/dirname on.
* @param boolean $dotfiles Show dotfiles?
* @param boolean $dironly Show only directories?
* @return array File list on success or PEAR_Error on failure.
function _listFolder ($path = '', $filter = null , $dotfiles = true ,
$conn = $this->_connect ();
if (is_a($conn, 'PEAR_Error')) {
/* If 'maplocalids' is set, check for the POSIX extension. */
if (!empty ($this->_params['maplocalids']) &&
// THIS IS A PROBLEM.... there is no builtin systype() fn for SSH2.
// Go with unix-style listings for now...
$res = $this->_setPath ($path);
if (is_a($res, 'PEAR_Error')) {
// Get numeric ids if we're going to use posix_* functions to
// If we don't want dotfiles, We can save work here by not doing
// an ls -a and then not doing the check later (by setting
// $dotfiles to true, the if is short-circuited).
$stream = @ssh2_exec ($this->_stream, 'LC_TIME=C ls -' . $ls_args . ' ' . escapeshellarg($path));
$stream = @ssh2_exec ($this->_stream, '');
$res = $this->_setPath ($olddir);
if (is_a($res, 'PEAR_Error')) {
foreach ($list as $line) {
if ((count($item) < 8 ) || (substr($line, 0 , 5 ) == 'total')) {
$file['perms'] = $item[0 ];
if (!isset ($this->_uids[$item[2 ]])) {
$this->_uids[$item[2 ]] = (empty ($entry)) ? $item[2 ] : $entry['name'];
$file['owner'] = $this->_uids[$item[2 ]];
if (!isset ($this->_uids[$item[3 ]])) {
$this->_uids[$item[3 ]] = (empty ($entry)) ? $item[3 ] : $entry['name'];
$file['group'] = $this->_uids[$item[3 ]];
$file['owner'] = $item[2 ];
$file['group'] = $item[3 ];
$file['name'] = substr($line, strpos($line, sprintf("%s %2s %5s", $item[5 ], $item[6 ], $item[7 ])) + 13 );
// Filter out '.' and '..' entries.
// Filter out dotfiles if they aren't wanted.
if (!$dotfiles && (substr($file['name'], 0 , 1 ) == '.')) {
$p1 = substr($file['perms'], 0 , 1 );
$file['link'] = substr($file['name'], strpos($file['name'], '->') + 3 );
$file['name'] = substr($file['name'], 0 , strpos($file['name'], '->') - 1 );
if ($this->isFolder('', $file['link'])) {
$file['linktype'] = '**dir';
$parts = explode('/', $file['link']);
if ((count($name) == 1 ) ||
(($name[0 ] === '') && (count($name) == 2 ))) {
$file['linktype'] = '**none';
$name = explode('.', $file['name']);
if ((count($name) == 1 ) ||
((substr($file['name'], 0 , 1 ) === '.') &&
$file['type'] = '**none';
if ($file['type'] == '**dir') {
$file['size'] = $item[4 ];
if (strpos($item[7 ], ':') !== false ) {
$file['date'] = strtotime($item[7 ] . ':00' . $item[5 ] . ' ' . $item[6 ] . ' ' . date('Y', $currtime));
// If the ssh2 server reports a file modification date more
// less than one day in the future, don't try to subtract
// a year from the date. There is no way to know, for
// example, if the VFS server and the ssh2 server reside
// in different timezones. We should simply report to the
// user what the SSH2 server is returning.
if ($file['date'] > ($currtime + 86400 )) {
$file['date'] = strtotime($item[7 ] . ':00' . $item[5 ] . ' ' . $item[6 ] . ' ' . (date('Y', $currtime) - 1 ));
$file['date'] = strtotime('00:00:00' . $item[5 ] . ' ' . $item[6 ] . ' ' . $item[7 ]);
} elseif ($type == 'netware') {
$file['perms'] = $item[1 ];
$file['owner'] = $item[2 ];
$file['type'] = '**none';
$file['size'] = $item[3 ];
$file['name'] = $item[7 ];
while ($index < count($item)) {
$file['name'] .= ' ' . $item[$index];
/* Handle Windows SSH2 servers returning DOS-style file
$file['name'] = $item[3 ];
while ($index < count($item)) {
$file['name'] .= ' ' . $item[$index];
$file['date'] = strtotime($item[0 ] . ' ' . $item[1 ]);
if ($item[2 ] == '<DIR>') {
$file['size'] = $item[2 ];
$name = explode('.', $file['name']);
if ((count($name) == 1 ) ||
((substr($file['name'], 0 , 1 ) === '.') &&
$file['type'] = '**none';
if ($this->_filterMatch ($filter, $file['name'])) {
if ($dironly && $file['type'] !== '**dir') {
$files[$file['name']] = $file;
$res = $this->_setPath ($olddir);
if (is_a($res, 'PEAR_Error')) {
* Returns a sorted list of folders in the specified directory.
* @param string $path The path of the directory to get the
* @param mixed $filter Hash of items to filter based on folderlist.
* @param boolean $dotfolders Include dotfolders?
* @return mixed Folder list on success or a PEAR_Error object on failure.
function listFolders($path = '', $filter = null , $dotfolders = true )
$conn = $this->_connect ();
if (is_a($conn, 'PEAR_Error')) {
$folderList = $this->listFolder($path, null , $dotfolders, true );
if (is_a($folderList, 'PEAR_Error')) {
$folder['val'] = $this->_parentDir ($path);
$folder['abbrev'] = $folder['label'] = '..';
$folders[$folder['val']] = $folder;
foreach ($folderList as $files) {
$folder['val'] = $this->_getPath($path, $files['name']);
$folder['abbrev'] = $files['name'];
$folder['label'] = $folder['val'];
$folders[$folder['val']] = $folder;
* Copies a file through the backend.
* @param string $path The path of the original file.
* @param string $name The name of the original file.
* @param string $dest The name of the destination directory.
* @param boolean $autocreate Auto-create the directory if it doesn't
* @return mixed True on success or a PEAR_Error object on failure.
function copy($path, $name, $dest, $autocreate = false )
return PEAR ::raiseError (_("Cannot copy file(s) - source and destination are the same."));
$conn = $this->_connect ();
if (is_a($conn, 'PEAR_Error')) {
if (is_a($res, 'PEAR_Error')) {
$fileCheck = $this->listFolder($dest, null , true );
if (is_a($fileCheck, 'PEAR_Error')) {
foreach ($fileCheck as $file) {
if ($file['name'] == $name) {
return PEAR ::raiseError (sprintf(_("%s already exists."), $this->_getPath($dest, $name)));
if (is_a($result, 'PEAR_Error')) {
$fetch = $this->_recv ($orig, $tmpFile);
return PEAR ::raiseError (sprintf(_("Failed to copy from \"%s\"."), $orig));
$res = $this->_checkQuotaWrite ('file', $tmpFile);
if (is_a($res, 'PEAR_Error')) {
if (!$this->_send ($tmpFile, $this->_getPath($dest, $name))) {
return PEAR ::raiseError (sprintf(_("Failed to copy to \"%s\"."), $this->_getPath($dest, $name)));
* Moves a file through the backend.
* @param string $path The path of the original file.
* @param string $name The name of the original file.
* @param string $dest The destination file name.
* @param boolean $autocreate Auto-create the directory if it doesn't
* @return mixed True on success or a PEAR_Error object on failure.
function move($path, $name, $dest, $autocreate = false )
return PEAR ::raiseError (_("Cannot move file(s) - destination is within source."));
$conn = $this->_connect ();
if (is_a($conn, 'PEAR_Error')) {
if (is_a($res, 'PEAR_Error')) {
$fileCheck = $this->listFolder($dest, null , true );
if (is_a($fileCheck, 'PEAR_Error')) {
foreach ($fileCheck as $file) {
if ($file['name'] == $name) {
return PEAR ::raiseError (sprintf(_("%s already exists."), $this->_getPath($dest, $name)));
if (!@ssh2_sftp_rename ($this->_sftp, $orig, $this->_getPath($dest, $name))) {
return PEAR ::raiseError (sprintf(_("Failed to move to \"%s\"."), $this->_getPath($dest, $name)));
* Returns the current working directory on the SSH2 server.
* @return string The current working directory.
if (is_a($res = $this->_connect (), 'PEAR_Error')) {
$stream = @ssh2_exec ($this->_stream, 'pwd');
* Changes the current directory on the server.
* @param string $path The path to change to.
* @return boolean True on success, or a PEAR_Error on failure.
if ($stream = @ssh2_exec ($this->_stream, 'cd ' . escapeshellarg($path) . '; pwd')) {
return PEAR ::raiseError (sprintf(_("Unable to change to %s."), $path));
* Returns the full path of an item.
* @param string $path The directory of the item.
* @param string $name The name of the item.
* @return mixed Full path to the file when $path is not empty and just
return ($path . '/' . $name);
* Returns the parent directory of the specified path.
* @param string $path The path to get the parent of.
* @return string The parent directory (string) on success or a PEAR_Error
function _parentDir ($path)
$conn = $this->_connect ();
if (is_a($conn, 'PEAR_Error')) {
$this->_setPath ('cd ' . $path . '/..');
* Attempts to open a connection to the SSH2 server.
* @return mixed True on success or a PEAR_Error object on failure.
if ($this->_stream === false ) {
return PEAR ::raiseError (_("The SSH2 PECL extension is not available."));
return PEAR ::raiseError (_("No configuration information specified for SSH2 VFS."));
$required = array ('hostspec', 'username', 'password');
foreach ($required as $val) {
if (!isset ($this->_params[$val])) {
return PEAR ::raiseError (sprintf(_("Required \"%s\" not specified in VFS configuration."), $val));
/* Connect to the ssh2 server using the supplied parameters. */
if (empty ($this->_params['port'])) {
$this->_stream = @ssh2_connect ($this->_params['hostspec']);
$this->_stream = @ssh2_connect ($this->_params['hostspec'], $this->_params['port']);
return PEAR ::raiseError (_("Connection to SSH2 server failed."));
$connected = @ssh2_auth_password ($this->_stream, $this->_params['username'], $this->_params['password']);
return PEAR ::raiseError (_("Authentication to SSH2 server failed."));
/* Create sftp resource. */
$this->_sftp = @ssh2_sftp ($this->_stream);
* Sends local file to remote host.
* This function exists because the php_scp_* functions doesn't seem to work on some hosts.
* @param string $local Full path to the local file.
* @param string $remote Full path to the remote location.
* @return boolean TRUE on success, FALSE on failure.
function _send ($local, $remote)
return @copy($local, $this->_wrap ($remote));
* Receives file from remote host.
* This function exists because the php_scp_* functions doesn't seem to work on some hosts.
* @param string $local Full path to the local file.
* @param string $remote Full path to the remote location.
* @return boolean TRUE on success, FALSE on failure.
function _recv ($remote, $local)
return @copy($this->_wrap ($remote), $local);
* Generate a stream wrapper file spec for a remote file path
* @param string $remote Full path to the remote location
* @return string A full stream wrapper path to the remote location
return 'ssh2.sftp://' . $this->_params['username'] . ':' . $this->_params['password']
. '@' . $this->_params['hostspec'] . ':' . $this->_params['port'] . $remote;
Documentation generated on Mon, 11 Mar 2019 15:34:56 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.
|