diff options
author | Dries Buytaert <dries@buytaert.net> | 2009-08-28 07:51:55 +0000 |
---|---|---|
committer | Dries Buytaert <dries@buytaert.net> | 2009-08-28 07:51:55 +0000 |
commit | b8448cac634bdc6d1e4c618a83d8478033734b9f (patch) | |
tree | 446b1d8daaf0c2df8c2bd08e50ddb75fd91e149b /includes/filetransfer | |
parent | 45da74801a093fe58017cd109bbb14ef0e9e4c21 (diff) | |
download | brdo-b8448cac634bdc6d1e4c618a83d8478033734b9f.tar.gz brdo-b8448cac634bdc6d1e4c618a83d8478033734b9f.tar.bz2 |
- Patch #528326 by JacobSingh, cwgordon7: clean-up and bug fixes for the FTP file transfer classes.
Diffstat (limited to 'includes/filetransfer')
-rw-r--r-- | includes/filetransfer/filetransfer.inc | 127 | ||||
-rw-r--r-- | includes/filetransfer/ftp.inc | 85 | ||||
-rw-r--r-- | includes/filetransfer/ssh.inc | 26 |
3 files changed, 221 insertions, 17 deletions
diff --git a/includes/filetransfer/filetransfer.inc b/includes/filetransfer/filetransfer.inc index 2b1c18c7e..f0cf9d56a 100644 --- a/includes/filetransfer/filetransfer.inc +++ b/includes/filetransfer/filetransfer.inc @@ -20,11 +20,7 @@ abstract class FileTransfer { * The constructer for the UpdateConnection class. This method is also called * from the classes that extend this class and override this method. */ - function __construct($jail, $username, $password, $hostname, $port) { - $this->username = $username; - $this->password = $password; - $this->hostname = $hostname; - $this->port = $port; + function __construct($jail) { $this->jail = $jail; } @@ -41,6 +37,11 @@ abstract class FileTransfer { $this->connect(); return $this->connection; } + + if ($name == 'chroot') { + $this->setChroot(); + return $this->chroot; + } } /** @@ -57,17 +58,36 @@ abstract class FileTransfer { * The destination path. */ public final function copyDirectory($source, $destination) { + $source = $this->sanitizePath($source); + $destination = $this->fixRemotePath($destination); $this->checkPath($destination); $this->copyDirectoryJailed($source, $destination); } /** + * @see http://php.net/chmod + * + * @param string $path + * @param long $mode + * @param bool $recursive + */ + public final function chmod($path, $mode, $recursive = FALSE) { + $path = $this->sanitizePath($path); + $path = $this->fixRemotePath($path); + $this->checkPath($path); + $this->chmodJailed($path, $mode, $recursive); + } + + protected abstract function chmodJailed($path, $mode, $recursive); + + /** * Creates a directory. * * @param $directory * The directory to be created. */ public final function createDirectory($directory) { + $directory = $this->fixRemotePath($directory); $this->checkPath($directory); $this->createDirectoryJailed($directory); } @@ -79,6 +99,7 @@ abstract class FileTransfer { * The directory to be removed. */ public final function removeDirectory($directory) { + $directory = $this->fixRemotePath($directory); $this->checkPath($directory); $this->removeDirectoryJailed($directory); } @@ -92,6 +113,8 @@ abstract class FileTransfer { * The destination file. */ public final function copyFile($source, $destination) { + $source = $this->sanitizePath($source); + $destination = $this->fixRemotePath($destination); $this->checkPath($destination); $this->copyFileJailed($source, $destination); } @@ -103,6 +126,7 @@ abstract class FileTransfer { * The destination file to be removed. */ public final function removeFile($destination) { + $destination = $this->fixRemotePath($destination); $this->checkPath($destination); $this->removeFileJailed($destination); } @@ -114,12 +138,51 @@ abstract class FileTransfer { * A path to check against the jail. */ protected final function checkPath($path) { - if (drupal_realpath(substr($path, 0, strlen($this->jail))) !== $this->jail) { + $full_jail = $this->chroot . $this->jail; + $full_path = drupal_realpath(substr($this->chroot . $path, 0, strlen($full_jail))); + $full_path = $this->fixRemotePath($full_path, FALSE); + if ($full_jail !== $full_path) { throw new FileTransferException('@directory is outside of the @jail', NULL, array('@directory' => $path, '@jail' => $this->jail)); } } /** + * Returns a modified path suitable for passing to the server. + * If a path is a windows path, makes it posix compliant by removing the drive letter. + * If $this->chroot has a value, it is stripped from the path to allow for + * chroot'd filetransfer systems. + * + * @param $path + * @param $strip_chroot + * + * @return string; + */ + protected final function fixRemotePath($path, $strip_chroot = TRUE) { + $path = $this->sanitizePath($path); + $path = preg_replace('|^([a-z]{1}):|i', '', $path); // Strip out windows driveletter if its there. + if ($strip_chroot) { + if ($this->chroot && strpos($path, $this->chroot) === 0) { + $path = ($path == $this->chroot) ? '' : substr($path, strlen($this->chroot)); + } + } + return $path; + } + + /** + * Changes backslahes to slashes, also removes a trailing slash. + * + * @param string $path + * @return string; + */ + function sanitizePath($path) { + $path = str_replace('\\', '/', $path); // Windows path sanitiation. + if (substr($path, -1) == '/') { + $path = substr($path, 0, -1); + } + return $path; + } + + /** * Copies a directory. * * We need a separate method to make the $destination is in the jail. @@ -178,7 +241,7 @@ abstract class FileTransfer { * The destination file to be removed. */ abstract protected function removeFileJailed($destination); - + /** * Checks if a particular path is a directory * @@ -188,6 +251,54 @@ abstract class FileTransfer { * @return boolean */ abstract public function isDirectory($path); + + /** + * Checks if a particular path is a file (not a directory). + * + * @param $path + * The path to check + * + * @return boolean + */ + abstract public function isFile($path); + + /** + * Gets the chroot property for this connection. It does this by moving up + * the tree until it finds itself. If successful, it will return a chroot. + * + * @return string chroot + */ + function findChroot() { + // If the file exists as is, there is no chroot. + $path = __FILE__; + $path = $this->fixRemotePath($path, FALSE); + if ($this->isFile($path)) { + return FALSE; + } + + $path = dirname(__FILE__); + $path = $this->fixRemotePath($path, FALSE); + $parts = explode('/', $path); + $chroot = ''; + while (count($parts)) { + $check = implode($parts, '/'); + if ($this->isFile($check . '/' . basename(__FILE__))) { + // Remove the trailing slash. + return substr($chroot,0,-1); + } + $chroot .= array_shift($parts) . '/'; + } + return FALSE; + } + + /** + * Sets the chroot and changes the jail to match the correct path scheme + * + */ + function setChroot() { + $this->chroot = $this->findChroot(); + $this->jail = $this->fixRemotePath($this->jail); + } } /** @@ -200,4 +311,4 @@ class FileTransferException extends Exception { parent::__construct($message, $code); $this->arguments = $arguments; } -} +}
\ No newline at end of file diff --git a/includes/filetransfer/ftp.inc b/includes/filetransfer/ftp.inc index 008a20c55..32e02901a 100644 --- a/includes/filetransfer/ftp.inc +++ b/includes/filetransfer/ftp.inc @@ -49,23 +49,56 @@ class FileTransferFTPWrapper extends FileTransfer { } function copyFileJailed($source, $destination) { - if (!@copy($this->connection . '/' . $source, $this->connection . '/' . $destination)) { + if (!@copy($source, $this->connection . '/' . $destination)) { throw new FileTransferException('Cannot copy @source_file to @destination_file.', NULL, array('@source' => $source, '@destination' => $destination)); } } function removeFileJailed($destination) { - if (!@unlink($destination)) { + if (!@unlink($this->connection . '/' .$destination)) { throw new FileTransferException('Cannot remove @destination', NULL, array('@destination' => $destination)); } } - + function isDirectory($path) { return is_dir($this->connection . '/' . $path); } + + public function isFile($path) { + // This is stupid, but is_file and file_exists don't work! always return true. + return @fopen($this->connection . '/' . $path,'r'); + } + + /** + * This is impossible with the stream wrapper, + * So we cheat and use the other implementation + * + * @staticvar FileTransferFTPExtension $ftp_ext_file_transfer + * @param string $path + * @param long $mode + * @param bool $recursive + */ + function chmodJailed($path, $mode, $recursive) { + static $ftp_ext_file_transfer; + + if (!$ftp_ext_file_transfer) { + $ftp_ext_file_transfer = new FileTransferFTPExtension($this->jail, $this->username, $this->password, $this->hostname, $this->port); + } + $ftp_ext_file_transfer->chmodJailed($path, $mode, $recursive); + } + } class FileTransferFTPExtension extends FileTransfer { + + public function __construct($jail, $username, $password, $hostname, $port) { + $this->username = $username; + $this->password = $password; + $this->hostname = $hostname; + $this->port = $port; + parent::__construct($jail); + } + public function connect() { $this->connection = ftp_connect($this->hostname, $this->port); @@ -76,7 +109,14 @@ class FileTransferFTPExtension extends FileTransfer { throw new FileTransferException("Cannot login to FTP server, please check username and password"); } } - + + /** + * Returns a copy of itself using common defaults. + * + * @param string $jail + * @param array $settings + * @return FileTransferFTPExtension + */ static function factory($jail, $settings) { $settings['hostname'] = empty($settings['hostname']) ? 'localhost' : $settings['hostname']; $settings['port'] = empty($settings['port']) ? 21 : $settings['port']; @@ -90,17 +130,20 @@ class FileTransferFTPExtension extends FileTransfer { } protected function createDirectoryJailed($directory) { - if (!@ftp_mkdir($this->connection, $directory)) { + if (!ftp_mkdir($this->connection, $directory)) { throw new FileTransferException("Cannot create directory @directory", NULL, array("@directory" => $directory)); } } protected function removeDirectoryJailed($directory) { $pwd = ftp_pwd($this->connection); - if (!@ftp_chdir($this->connection, $directory)) { + if (!ftp_chdir($this->connection, $directory)) { throw new FileTransferException("Unable to change to directory @directory", NULL, array('@directory' => $directory)); } $list = @ftp_nlist($this->connection, '.'); + if (!$list) { + $list = array(); + } foreach ($list as $item){ if ($item == '.' || $item == '..') { continue; @@ -124,14 +167,40 @@ class FileTransferFTPExtension extends FileTransfer { throw new FileTransferException("Unable to remove to file @file", NULL, array('@file' => $destination)); } } - + public function isDirectory($path) { $result = FALSE; $curr = ftp_pwd($this->connection); - if (ftp_chdir($this->connection, $path)) { + if (@ftp_chdir($this->connection, $path)) { $result = TRUE; } ftp_chdir($this->connection, $curr); return $result; } + + public function isFile($path) { + return ftp_size($this->connection, $path) != -1; + } + + function chmodJailed($path, $mode, $recursive) { + if (!ftp_chmod($this->connection, $mode, $path)) { + throw new FileTransferException("Unable to set permissions on %file", NULL, array ('%file' => $path)); + } + if ($this->isDirectory($path) && $recursive) { + $filelist = @ftp_nlist($this->connection, $path); + if (!$filelist) { + //empty directory - returns false + return; + } + foreach ($filelist as $file) { + $this->chmodJailed($file, $mode, $recursive); + } + } + } } + +if (!function_exists('ftp_chmod')) { + function ftp_chmod($ftp_stream, $mode, $filename) { + return ftp_site($ftp_stream, sprintf('CHMOD %o %s', $mode, $filename)); + } +}
\ No newline at end of file diff --git a/includes/filetransfer/ssh.inc b/includes/filetransfer/ssh.inc index e6148c8f0..774e136d6 100644 --- a/includes/filetransfer/ssh.inc +++ b/includes/filetransfer/ssh.inc @@ -7,7 +7,11 @@ class FileTransferSSH extends FileTransfer { function __construct($jail, $username, $password, $hostname = "localhost", $port = 22) { - parent::__construct($jail, $username, $password, $hostname, $port); + $this->username = $username; + $this->password = $password; + $this->hostname = $hostname; + $this->port = $port; + parent::__construct($jail); } function connect() { @@ -71,4 +75,24 @@ class FileTransferSSH extends FileTransfer { throw new FileTransferException('Cannot check @path.', NULL, array('@path' => $path)); } } + + public function isFile($path) { + $file = escapeshellarg($path); + $cmd = "[ -f {$file} ] && echo 'yes'"; + if ($output = @ssh2_exec($this->connection, $cmd)) { + if ($output == 'yes') { + return TRUE; + } + return FALSE; + } else { + throw new FileTransferException('Cannot check @path.', NULL, array('@path' => $path)); + } + } + + function chmodJailed($path, $mode, $recursive) { + $cmd = sprintf("chmod %s%o %s", $recursive ? '-R ' : '', $mode, escapeshellarg($path)); + if (@!ssh2_exec($this->connection, $cmd)) { + throw new FileTransferException('Cannot change permissions of @path.', NULL, array('@path' => $path)); + } + } } |