diff options
author | Dries Buytaert <dries@buytaert.net> | 2009-06-23 12:11:19 +0000 |
---|---|---|
committer | Dries Buytaert <dries@buytaert.net> | 2009-06-23 12:11:19 +0000 |
commit | d7e2be1520b45edb09da11df52fb307d0ec906ab (patch) | |
tree | 1a056f73dbd6dc56d3431997c4678a18fd90bb56 /includes/filetransfer | |
parent | c84c1d9c340687bc008e199fc4a9afd1208a5b37 (diff) | |
download | brdo-d7e2be1520b45edb09da11df52fb307d0ec906ab.tar.gz brdo-d7e2be1520b45edb09da11df52fb307d0ec906ab.tar.bz2 |
- Patch #395472 by chx, dww, cwgordon7, JacobSingh, et al: added different file transport mechanisms to core in preparation of a plugin manager.
Diffstat (limited to 'includes/filetransfer')
-rw-r--r-- | includes/filetransfer/filetransfer.inc | 102 | ||||
-rw-r--r-- | includes/filetransfer/ftp.inc | 128 | ||||
-rw-r--r-- | includes/filetransfer/ssh.inc | 57 |
3 files changed, 287 insertions, 0 deletions
diff --git a/includes/filetransfer/filetransfer.inc b/includes/filetransfer/filetransfer.inc new file mode 100644 index 000000000..2fb769913 --- /dev/null +++ b/includes/filetransfer/filetransfer.inc @@ -0,0 +1,102 @@ +<?php +// $Id$ + +/* + * Connection class. + * + * This class does file operations on directories not writeable by the + * webserver. It connects back to the server using some backend (for example + * FTP or SSH). To keep security the password should always be asked from the + * user and never stored. + */ +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($settings) { + $this->username = $settings['username']; + $this->password = $settings['password']; + $this->hostname = isset($settings['hostname']) ? $settings['hostname'] : 'localhost'; + if (isset($settings['port'])) { + $this->port = $settings['port']; + } + } + + /** + * Implementation of the magic __get() method. If the connection isn't set to + * anything, this will call the connect() method and set it to and return the + * result; afterwards, the connection will be returned directly without using + * this method. + */ + function __get($name) { + static $connection; + if ($name == 'connection') { + $this->connection = $this->connect(); + return $this->connection; + } + } + + /** + * Copies a directory. + * + * @param $source + * The source path. + * @param $destination + * The destination path. + */ + protected function copyDirectory($source, $destination) { + $this->createDirectory($destination . basename($source)); + foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST) as $filename => $file) { + $relative_path = basename($source) . substr($filename, strlen($source)); + if ($file->isDir()) { + $this->createDirectory($destination . $relative_path); + } + else { + $this->copyFile($file->getPathName(), $destination . $relative_path); + } + } + } + + /** + * Creates a directory. + * + * @param $directory + * The directory to be created. + */ + abstract function createDirectory($directory); + + /** + * Removes a directory. + * + * @param $directory + * The directory to be removed. + */ + abstract function removeDirectory($directory); + + /** + * Copies a file. + * + * @param $source + * The source file. + * @param $destination + * The destination file. + */ + abstract function copyFile($source, $destination); + + + /** + * Removes a file. + * + * @param $destination + * The destination file to be removed. + */ + abstract function removeFile($destination); +} + +/** + * FileTransferException class. + */ +class FileTransferException extends Exception { +} diff --git a/includes/filetransfer/ftp.inc b/includes/filetransfer/ftp.inc new file mode 100644 index 000000000..d21e88bec --- /dev/null +++ b/includes/filetransfer/ftp.inc @@ -0,0 +1,128 @@ +<?php +// $Id$ + +/** + * Common code for the FTP connections. + */ +abstract class FileTransferFTP extends FileTransfer { + function __construct($settings) { + // This is the default, if $settings contains a port, this will be overridden. + $this->port = 21; + parent::__construct($settings); + } +} + +/** + * Connection class using the FTP URL wrapper. + */ +class FileTransferFTPWrapper extends FileTransfer { + function connect() { + $this->connection = 'ftp://' . urlencode($this->username) . ':' . urlencode($this->password) . '@' . $this->hostname . ':' . $this->port . '/'; + if (!is_dir($this->connection)) { + throw new FileTransferException('FTP Connection failed.'); + } + } + + function createDirectory($directory) { + if (!@createDirectory($directory)) { + $exception = new FileTransferException('Cannot create directory @directory.', NULL, array('@directory' => $directory)); + throw $exception; + } + } + + function removeDirectory($directory) { + if (realpath(substr($directory, 0, strlen(DRUPAL_ROOT))) !== DRUPAL_ROOT) { + throw new FileTransferException('@directory is outside of the Drupal root.', NULL, array('@directory' => $directory)); + } + if (is_dir($directory)) { + $dh = opendir($directory); + while (($resource = readdir($dh)) !== FALSE) { + if ($resource == '.' || $resource == '..') { + continue; + } + $full_path = $directory . DIRECTORY_SEPARATOR . $resource; + if (is_file($full_path)) { + $this->removeFile($full_path); + } + elseif (is_dir($full_path)) { + $this->removeDirectory($full_path . '/'); + } + } + closedir($dh); + if (!removeDirectory($directory)) { + $exception = new FileTransferException('Cannot remove @directory.', NULL, array('@directory' => $directory)); + throw $exception; + } + } + } + + function copyFile($source, $destination) { + if (!@copy($this->connection . '/' . $source, $this->connection . '/' . $destination)) { + throw new FileTransferException('Cannot copy @source_file to @destination_file.', NULL, array('@source' => $source, '@destination' => $destination)); + } + } + + function removeFile($destination) { + if (!@unlink($destination)) { + throw new FileTransferException('Cannot remove @destination', NULL, array('@destination' => $destination)); + } + } +} + +class FileTransferFTPExtension extends FileTransfer { + function connect() { + $this->connection = ftp_connect($this->hostname, $this->port); + + if (!$this->connection) { + throw new FileTransferException("Cannot connect to FTP Server, please check settings"); + } + if (!ftp_login($this->connection, $this->username, $this->password)) { + throw new FileTransferException("Cannot login to FTP server, please check username and password"); + } + } + + function copyFile($source, $destination) { + if (!@ftp_put($this->connection, $destination, $source, FTP_BINARY)) { + throw new FileTransferException("Cannot move @source to @destination", NULL, array("@source" => $source, "@destination" => $destination)); + } + } + + function createDirectory($directory) { + if (!@ftp_createDirectory($this->connection, $directory)) { + throw new FileTransferException("Cannot create directory @directory", NULL, array("@directory" => $directory)); + } + } + + function removeDirectory($directory) { + if (realpath(substr($directory, 0, strlen(DRUPAL_ROOT))) !== DRUPAL_ROOT) { + throw new FileTransferException('@directory is outside of the Drupal root.', NULL, array('@directory' => $directory)); + } + $pwd = ftp_pwd($this->connection); + if (!@ftp_chdir($this->connection, $directory)) { + throw new FileTransferException("Unable to change to directory @directory", NULL, array('@directory' => $directory)); + } + $list = @ftp_nlist($this->connection, '.'); + foreach ($list as $item){ + if ($item == '.' || $item == '..') { + continue; + } + if (@ftp_chdir($this->connection, $item)){ + ftp_chdir($this->connection, '..'); + $this->removeDirectory($item); + } + else { + $this->removeFile($item); + } + } + ftp_chdir($this->connection, $pwd); + if (!ftp_removeDirectory($this->connection, $directory)) { + throw new FileTransferException("Unable to remove to directory @directory", NULL, array('@directory' => $directory)); + } + } + + function removeFile($destination) { + if (!ftp_delete($this->connection, $item)) { + throw new FileTransferException("Unable to remove to file @file", NULL, array('@file' => $item)); + } + } +} diff --git a/includes/filetransfer/ssh.inc b/includes/filetransfer/ssh.inc new file mode 100644 index 000000000..846b976dd --- /dev/null +++ b/includes/filetransfer/ssh.inc @@ -0,0 +1,57 @@ +<?php +// $Id$ + +/** + * The SSH connection class for the update module. + */ +class FileTransferSSH extends FileTransfer { + + function __construct($settings) { + // This is the default, if $settings contains a port, this will be overridden. + $this->port = 22; + parent::__construct($settings); + } + + function connect() { + $this->connection = @ssh2_connect($setings['hostname'], $this->port); + if (!$this->connection) { + throw new FileTransferException('SSH Connection failed.'); + } + if (!@ssh2_auth_password($this->connection, $this->username, $this->password)) { + throw new FileTransferException('The supplied username/password combination was not accepted.'); + } + } + + function copyFile($source, $destination) { + if (!@ssh2_scp_send($this->connection, $source, $destination)) { + throw new FileTransferException('Cannot copy @source_file to @destination_file.', NULL, array('@source' => $source, '@destination' => $destination)); + } + } + + function copyDirectory($source, $destination) { + if (!@ssh2_exec($this->connection, 'cp -Rp ' . escapeshellarg($source) . ' ' . escapeshellarg($destination))) { + throw new FileTransferException('Cannot copy directory @directory.', NULL, array('@directory' => $source)); + } + } + + function createDirectory($directory) { + if (!@ssh2_exec($this->connection, 'mkdir ' . escapeshellarg($directory))) { + throw new FileTransferException('Cannot create directory @directory.', NULL, array('@directory' => $directory)); + } + } + + function removeDirectory($directory) { + if (realpath(substr($directory, 0, strlen(DRUPAL_ROOT))) !== DRUPAL_ROOT) { + throw new FileTransferException('@directory is outside of the Drupal root.', NULL, array('@directory' => $directory)); + } + if (!@ssh2_exec($this->connection, 'rm -Rf ' . escapeshellarg($directory))) { + throw new FileTransferException('Cannot remove @directory.', NULL, array('@directory' => $directory)); + } + } + + function removeFile($destination) { + if (!@ssh2_exec($this->connection, 'rm ' . escapeshellarg($destination))) { + throw new FileTransferException('Cannot remove @directory.', NULL, array('@directory' => $destination)); + } + } +} |