Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.715 diff -n -u -r1.715 system.module --- modules/system/system.module 23 Jun 2009 12:11:19 -0000 1.715 +++ modules/system/system.module 24 Jun 2009 12:15:58 -0000 @@ -827,9 +827,6 @@ return !empty($content); } -/** - * Implementation of hook_filetransfer_backends(). - */ function system_filetransfer_backends() { $backends = array(); @@ -847,7 +844,6 @@ 'class' => 'FileTransferFTPExtension', ); } - if (ini_get('allow_url_fopen')) { $backends['ftp_wrapper'] = array( 'title' => t('FTP Wrapper'), @@ -857,9 +853,44 @@ return $backends; } -/** - * Implement hook_init(). - */ +function system_filetransfer_backend_form($type, $show_non_stored_fields = FALSE) { + $form = array(); + + $form['hostname'] = array ( + '#type' => 'textfield', + '#title' => t('Host'), + '#default_value' => 'localhost', + ); + + $form['port'] = array ( + '#type' => 'textfield', + '#title' => t('Port'), + '#default_value' => NULL, + ); + + $form['username'] = array ( + '#type' => 'textfield', + '#title' => t('Username'), + ); + + $form['password'] = array ( + '#type' => 'password', + '#title' => t('Password'), + '#description' => t('This is not saved in the database and is only used to test the connection'), + ); + + switch ($type) { + case 'ssh': + $form['port']['#default_value'] = 22; + break; + case 'ftp_wrapper': + case 'ftp_extension': + $form['port']['#default_value'] = 21; + break; + } + return $form; +} + function system_init() { // Use the administrative theme if the user is looking at a page in the admin/* path. if (arg(0) == 'admin' || (variable_get('node_admin_theme', '0') && arg(0) == 'node' && (arg(1) == 'add' || arg(2) == 'edit'))) { @@ -2543,29 +2574,37 @@ /** * Attempts to get a file using drupal_http_request and to store it locally. * - * @param $path + * @param $url * The URL of the file to grab. + * + * @param $destination + * Where the file should be saved, if a directory is provided, file is saved + * in that directory with its original name. If a filename is provided, + * remote fileis stored to that location. NOTE: Relative to drupal "files" directory" + * + * @param $overwrite boolean + * Defaults to TRUE, will overwrite existing files of the same name. + * * @return * On success the address the files was saved to, FALSE on failure. */ -function system_retrieve_file($path) { - // Get each of the specified files. - $parsed_url = parse_url($path); - $local = file_directory_temp() . '/update-cache/' . basename($parsed_url['path']); - if (!file_exists(file_directory_temp() . '/update-cache/')) { - mkdir(file_directory_temp() . '/update-cache/'); - } - - // Check the cache and download the file if needed. - if (!file_exists($local)) { - // $result->data is the actual contents of the downloaded file. This saves - // it into a local file, whose path is stored in $local. $local is stored - // relative to the Drupal installation. - $result = drupal_http_request($path); - if ($result->code != 200 || !file_save_data($result->data, $local)) { - drupal_set_message(t('@remote could not be saved.', array('@remote' => $path)), 'error'); - return FALSE; - } +function system_retrieve_file($url, $destination = NULL, $overwrite = TRUE) { + if (!$destination) { + $destination = file_directory_temp(); + } + $parsed_url = parse_url($url); + $local = is_dir(file_directory_path() . '/' . $destination) ? $destination . '/' . basename($parsed_url['path']) : $destination; + + if (!$overwrite && file_exists($local)) { + drupal_set_message(t('@remote could not be saved. @local already exists', array('@remote' => $url, '@local' => $local)), 'error'); + return FALSE; + } + + $result = drupal_http_request($url); + if ($result->code != 200 || !file_save_data($result->data, $local)) { + drupal_set_message(t('@remote could not be saved.', array('@remote' => $url)), 'error'); + return FALSE; } + return $local; -} +} \ No newline at end of file Index: modules/system/system.info =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.info,v retrieving revision 1.14 diff -n -u -r1.14 system.info --- modules/system/system.info 17 Jun 2009 10:46:49 -0000 1.14 +++ modules/system/system.info 24 Jun 2009 12:15:58 -0000 @@ -10,5 +10,6 @@ files[] = image.gd.inc files[] = system.install files[] = system.test +files[] = filetransfer.test files[] = system.tar.inc required = TRUE Index: includes/filetransfer/filetransfer.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/filetransfer/filetransfer.inc,v retrieving revision 1.1 diff -n -u -r1.1 filetransfer.inc --- includes/filetransfer/filetransfer.inc 23 Jun 2009 12:11:19 -0000 1.1 +++ includes/filetransfer/filetransfer.inc 24 Jun 2009 12:15:59 -0000 @@ -1,8 +1,8 @@ jail = $jail; + } + /** - * The constructer for the UpdateConnection class. This method is also called - * from the classes that extend this class and override this method. + * @param string path + * A path to check against the jail. + * + * Throws a FileTransferException if the path is outside of the jail. */ - 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']; + function checkPath($path){ + if (!$this->jail) { + throw new FileTransferException('Jail was not provided'); + } + if (realpath(substr($path, 0, strlen($this->jail))) !== $this->jail) { + throw new FileTransferException('@directory is outside of the @jail', NULL, array('@directory' => $directory, '@jail' => $this->jail)); } } @@ -31,9 +39,8 @@ * this method. */ function __get($name) { - static $connection; if ($name == 'connection') { - $this->connection = $this->connect(); + $this->connect(); return $this->connection; } } @@ -46,19 +53,41 @@ * @param $destination * The destination path. */ - protected function copyDirectory($source, $destination) { - $this->createDirectory($destination . basename($source)); + public function copyDirectory($source, $destination) { + $this->checkPath($destination); + if ($this->canCopyDirectories()) { + $this->_copyDirectory($source, $destination); + return; + } + $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); + $this->createDirectory($destination . '/' . $relative_path); } else { - $this->copyFile($file->getPathName(), $destination . $relative_path); + $this->copyFile($file->getPathName(), $destination . '/' . $relative_path); } } } - + + public function removeDirectory($source, $destination) { + $this->checkPath($destination); + $this->_removeDirectory($source, $destination); + } + + protected function canCopyDirectories() { + if (method_exists($this,'_copyDirectory')) { + return TRUE; + } + } + + /** + * Initiates a connection to the remote location + * + */ + abstract function connect(); + /** * Creates a directory. * @@ -73,7 +102,7 @@ * @param $directory * The directory to be removed. */ - abstract function removeDirectory($directory); + abstract protected function _removeDirectory($directory); /** * Copies a file. @@ -84,8 +113,7 @@ * The destination file. */ abstract function copyFile($source, $destination); - - + /** * Removes a file. * @@ -93,10 +121,21 @@ * The destination file to be removed. */ abstract function removeFile($destination); + + } /** * FileTransferException class. */ class FileTransferException extends Exception { + var $params = array(); + function __construct($message, $code, $params = array()) { + parent::__construct($message, $code); + $this->params = $params; + } + + public function getParams() { + return $this->params; + } } Index: includes/filetransfer/ftp.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/filetransfer/ftp.inc,v retrieving revision 1.3 diff -n -u -r1.3 ftp.inc --- includes/filetransfer/ftp.inc 24 Jun 2009 01:45:09 -0000 1.3 +++ includes/filetransfer/ftp.inc 24 Jun 2009 12:15:59 -0000 @@ -4,18 +4,25 @@ /** * Common code for the FTP connections. */ -abstract class FileTransferFTP extends FileTransfer { - function __construct($settings) { +class FileTransferFTP extends FileTransfer { + function __construct($settings, $jail) { + $this->setJail($jail); + + $this->username = $settings['username']; + $this->password = $settings['password']; + $this->hostname = isset($settings['hostname']) ? $settings['hostname'] : 'localhost'; // This is the default, if $settings contains a port, this will be overridden. $this->port = 21; - parent::__construct($settings); + if (isset($settings['port'])) { + $this->port = $settings['port']; + } } } /** * Connection class using the FTP URL wrapper. */ -class FileTransferFTPWrapper extends FileTransfer { +class FileTransferFTPWrapper extends FileTransferFTP { function connect() { $this->connection = 'ftp://' . urlencode($this->username) . ':' . urlencode($this->password) . '@' . $this->hostname . ':' . $this->port . '/'; if (!is_dir($this->connection)) { @@ -30,10 +37,7 @@ } } - 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)); - } + protected function _removeDirectory($directory) { if (is_dir($directory)) { $dh = opendir($directory); while (($resource = readdir($dh)) !== FALSE) { @@ -69,7 +73,7 @@ } } -class FileTransferFTPExtension extends FileTransfer { +class FileTransferFTPExtension extends FileTransferFTP { function connect() { $this->connection = ftp_connect($this->hostname, $this->port); @@ -93,10 +97,7 @@ } } - 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)); - } + protected function _removeDirectory($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)); Index: includes/filetransfer/ssh.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/filetransfer/ssh.inc,v retrieving revision 1.1 diff -n -u -r1.1 ssh.inc --- includes/filetransfer/ssh.inc 23 Jun 2009 12:11:19 -0000 1.1 +++ includes/filetransfer/ssh.inc 24 Jun 2009 12:15:59 -0000 @@ -1,19 +1,26 @@ setJail($jail); + + $this->username = $settings['username']; + $this->password = $settings['password']; + $this->hostname = isset($settings['hostname']) ? $settings['hostname'] : 'localhost'; // This is the default, if $settings contains a port, this will be overridden. $this->port = 22; - parent::__construct($settings); + if (isset($settings['port'])) { + $this->port = $settings['port']; + } } function connect() { - $this->connection = @ssh2_connect($setings['hostname'], $this->port); + $this->connection = @ssh2_connect($this->hostname, $this->port); if (!$this->connection) { throw new FileTransferException('SSH Connection failed.'); } @@ -28,7 +35,7 @@ } } - function copyDirectory($source, $destination) { + private 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)); } @@ -40,10 +47,7 @@ } } - 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)); - } + protected function _removeDirectory($directory) { if (!@ssh2_exec($this->connection, 'rm -Rf ' . escapeshellarg($directory))) { throw new FileTransferException('Cannot remove @directory.', NULL, array('@directory' => $directory)); }