summaryrefslogtreecommitdiff
path: root/includes/filetransfer
diff options
context:
space:
mode:
authorDries Buytaert <dries@buytaert.net>2009-08-28 07:51:55 +0000
committerDries Buytaert <dries@buytaert.net>2009-08-28 07:51:55 +0000
commitb8448cac634bdc6d1e4c618a83d8478033734b9f (patch)
tree446b1d8daaf0c2df8c2bd08e50ddb75fd91e149b /includes/filetransfer
parent45da74801a093fe58017cd109bbb14ef0e9e4c21 (diff)
downloadbrdo-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.inc127
-rw-r--r--includes/filetransfer/ftp.inc85
-rw-r--r--includes/filetransfer/ssh.inc26
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));
+ }
+ }
}