Commit e89ce282 authored by catch's avatar catch
Browse files

Revert "Issue #1323124 by amateescu, aspilicious, Berdir: Convert file transfer system to PSR-0."

This reverts commit 5abec889.
parent 5abec889
......@@ -320,6 +320,10 @@ function authorize_get_filetransfer($backend, $settings = array()) {
$filetransfer = FALSE;
if (!empty($_SESSION['authorize_filetransfer_info'][$backend])) {
$backend_info = $_SESSION['authorize_filetransfer_info'][$backend];
if (!empty($backend_info['file'])) {
$file = $backend_info['file path'] . '/' . $backend_info['file'];
require_once $file;
}
if (class_exists($backend_info['class'])) {
$filetransfer = $backend_info['class']::factory(DRUPAL_ROOT, $settings);
}
......
......@@ -7535,14 +7535,28 @@ function drupal_get_updaters() {
* @return
* The Drupal FileTransfer class registry.
*
* @see Drupal\Core\FileTransfer\FileTransfer
* @see FileTransfer
* @see hook_filetransfer_info()
* @see hook_filetransfer_info_alter()
*/
function drupal_get_filetransfer_info() {
$info = &drupal_static(__FUNCTION__);
if (!isset($info)) {
$info = module_invoke_all('filetransfer_info');
// Since we have to manually set the 'file path' default for each
// module separately, we can't use module_invoke_all().
$info = array();
foreach (module_implements('filetransfer_info') as $module) {
$function = $module . '_filetransfer_info';
$result = $function();
if (isset($result) && is_array($result)) {
foreach ($result as &$values) {
if (empty($values['file path'])) {
$values['file path'] = drupal_get_path('module', $module);
}
}
$info = array_merge_recursive($info, $result);
}
}
drupal_alter('filetransfer_info', $info);
uasort($info, 'drupal_sort_weight');
}
......
......@@ -2,16 +2,7 @@
/**
* @file
* Definition of Drupal\Core\FileTransfer\FileTransfer.
*/
namespace Drupal\Core\FileTransfer;
use RecursiveIteratorIterator;
use RecursiveDirectoryIterator;
/**
* Defines the base FileTransfer class.
* Base FileTransfer class.
*
* Classes extending this class perform file operations on directories not
* writable by the webserver. To achieve this, the class should connect back
......@@ -20,52 +11,21 @@
* safety, all methods operate only inside a "jail", by default the Drupal root.
*/
abstract class FileTransfer {
/**
* The username for this file transfer.
*
* @var string
*/
protected $username;
/**
* The password for this file transfer.
*
* @var string
*/
protected $password;
/**
* The hostname for this file transfer.
*
* @var string
*/
protected $hostname = 'localhost';
/**
* The port for this file transfer.
*
* @var int
*/
protected $port;
/**
* Constructs a Drupal\Core\FileTransfer\FileTransfer object.
*
* @param $jail
* The full path where all file operations performed by this object will
* be restricted to. This prevents the FileTransfer classes from being
* able to touch other parts of the filesystem.
* The constructor for the UpdateConnection class. This method is also called
* from the classes that extend this class and override this method.
*/
function __construct($jail) {
$this->jail = $jail;
}
/**
* Defines a factory method for this class.
*
* Classes that extend this class must override the factory() static method.
* They should return a new instance of the appropriate FileTransfer subclass.
*
* @param string $jail
* The full path where all file operations performed by this object will
......@@ -75,28 +35,19 @@ function __construct($jail) {
* An array of connection settings for the FileTransfer subclass. If the
* getSettingsForm() method uses any nested settings, the same structure
* will be assumed here.
*
* @return object
* New instance of the appropriate FileTransfer subclass.
*
* @throws Drupal\Core\FileTransfer\FileTransferException
*/
static function factory($jail, $settings) {
throw new FileTransferException('FileTransfer::factory() static method not overridden by FileTransfer subclass.');
}
/**
* Implements the magic __get() method.
*
* If the connection isn't set to anything, this will call the connect()
* method and return the result; afterwards, the connection will be returned
* directly without using this method.
* Implementation of the magic __get() method.
*
* @param string $name
* The name of the variable to return.
*
* @return string|bool
* The variable specified in $name.
* 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) {
if ($name == 'connection') {
......@@ -118,9 +69,9 @@ abstract protected function connect();
/**
* Copies a directory.
*
* @param string $source
* @param $source
* The source path.
* @param string $destination
* @param $destination
* The destination path.
*/
public final function copyDirectory($source, $destination) {
......@@ -131,21 +82,14 @@ public final function copyDirectory($source, $destination) {
}
/**
* Changes the permissions of the specified $path (file or directory).
* @see http://php.net/chmod
*
* @param string $path
* The file / directory to change the permissions of.
* @param int $mode
* See the $mode argument from http://php.net/chmod.
* @param long $mode
* @param bool $recursive
* Pass TRUE to recursively chmod the entire directory specified in $path.
*
* @throws Drupal\Core\FileTransfer\FileTransferException
*
* @see http://php.net/chmod
*/
public final function chmod($path, $mode, $recursive = FALSE) {
if (!in_array('Drupal\Core\FileTransfer\ChmodInterface', class_implements(get_class($this)))) {
if (!in_array('FileTransferChmodInterface', class_implements(get_class($this)))) {
throw new FileTransferException('Unable to change file permissions');
}
$path = $this->sanitizePath($path);
......@@ -157,7 +101,7 @@ public final function chmod($path, $mode, $recursive = FALSE) {
/**
* Creates a directory.
*
* @param string $directory
* @param $directory
* The directory to be created.
*/
public final function createDirectory($directory) {
......@@ -169,7 +113,7 @@ public final function createDirectory($directory) {
/**
* Removes a directory.
*
* @param string $directory
* @param $directory
* The directory to be removed.
*/
public final function removeDirectory($directory) {
......@@ -181,9 +125,9 @@ public final function removeDirectory($directory) {
/**
* Copies a file.
*
* @param string $source
* @param $source
* The source file.
* @param string $destination
* @param $destination
* The destination file.
*/
public final function copyFile($source, $destination) {
......@@ -196,7 +140,7 @@ public final function copyFile($source, $destination) {
/**
* Removes a file.
*
* @param string $destination
* @param $destination
* The destination file to be removed.
*/
public final function removeFile($destination) {
......@@ -208,10 +152,8 @@ public final function removeFile($destination) {
/**
* Checks that the path is inside the jail and throws an exception if not.
*
* @param string $path
* @param $path
* A path to check against the jail.
*
* @throws Drupal\Core\FileTransfer\FileTransferException
*/
protected final function checkPath($path) {
$full_jail = $this->chroot . $this->jail;
......@@ -224,18 +166,14 @@ protected final function checkPath($path) {
/**
* 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.
*
* If a path is a windows path, makes it POSIX compliant by removing the drive
* letter. If $this->chroot has a value and $strip_chroot is TRUE, it is
* stripped from the path to allow for chroot'd filetransfer systems.
*
* @param string $path
* The path to modify.
* @param bool $strip_chroot
* Whether to remove the path in $this->chroot.
* @param $path
* @param $strip_chroot
*
* @return string
* The modified path.
*/
protected final function fixRemotePath($path, $strip_chroot = TRUE) {
$path = $this->sanitizePath($path);
......@@ -252,10 +190,7 @@ protected final function fixRemotePath($path, $strip_chroot = TRUE) {
* Changes backslashes to slashes, also removes a trailing slash.
*
* @param string $path
* The path to modify.
*
* @return string
* The modified path.
*/
function sanitizePath($path) {
$path = str_replace('\\', '/', $path); // Windows path sanitization.
......@@ -268,11 +203,11 @@ function sanitizePath($path) {
/**
* Copies a directory.
*
* We need a separate method to make sure the $destination is in the jail.
* We need a separate method to make the $destination is in the jail.
*
* @param string $source
* @param $source
* The source path.
* @param string $destination
* @param $destination
* The destination path.
*/
protected function copyDirectoryJailed($source, $destination) {
......@@ -280,7 +215,7 @@ protected function copyDirectoryJailed($source, $destination) {
$destination = $destination . '/' . drupal_basename($source);
}
$this->createDirectory($destination);
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST) as $filename => $file) {
foreach (new RecursiveIteratorIterator(new SkipDotsRecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST) as $filename => $file) {
$relative_path = substr($filename, strlen($source));
if ($file->isDir()) {
$this->createDirectory($destination . $relative_path);
......@@ -294,7 +229,7 @@ protected function copyDirectoryJailed($source, $destination) {
/**
* Creates a directory.
*
* @param string $directory
* @param $directory
* The directory to be created.
*/
abstract protected function createDirectoryJailed($directory);
......@@ -302,7 +237,7 @@ abstract protected function createDirectoryJailed($directory);
/**
* Removes a directory.
*
* @param string $directory
* @param $directory
* The directory to be removed.
*/
abstract protected function removeDirectoryJailed($directory);
......@@ -310,9 +245,9 @@ abstract protected function removeDirectoryJailed($directory);
/**
* Copies a file.
*
* @param string $source
* @param $source
* The source file.
* @param string $destination
* @param $destination
* The destination file.
*/
abstract protected function copyFileJailed($source, $destination);
......@@ -320,40 +255,39 @@ abstract protected function copyFileJailed($source, $destination);
/**
* Removes a file.
*
* @param string $destination
* @param $destination
* The destination file to be removed.
*/
abstract protected function removeFileJailed($destination);
/**
* Checks if a particular path is a directory.
* Checks if a particular path is a directory
*
* @param string $path
* @param $path
* The path to check
*
* @return bool
* TRUE if the specified path is a directory, FALSE otherwise.
* @return boolean
*/
abstract public function isDirectory($path);
/**
* Checks if a particular path is a file (not a directory).
*
* @param string $path
* The path to check.
* @param $path
* The path to check
*
* @return bool
* TRUE if the specified path is a file, FALSE otherwise.
* @return boolean
*/
abstract public function isFile($path);
/**
* Returns the chroot property for this connection.
*
* It does this by moving up the tree until it finds itself
* It does this by moving up the tree until it finds itself. If successful,
* it will return the chroot, otherwise FALSE.
*
* @return string|bool
* If successful, the chroot path for this connection, otherwise FALSE.
* @return
* The chroot path for this connection or FALSE.
*/
function findChroot() {
// If the file exists as is, there is no chroot.
......@@ -379,7 +313,8 @@ function findChroot() {
}
/**
* Sets the chroot and changes the jail to match the correct path scheme.
* Sets the chroot and changes the jail to match the correct path scheme
*
*/
function setChroot() {
$this->chroot = $this->findChroot();
......@@ -391,9 +326,6 @@ function setChroot() {
*
* Implementing classes can either extend this form with fields collecting the
* specific information they need, or override it entirely.
*
* @return array
* An array that contains a Form API definition.
*/
public function getSettingsForm() {
$form['username'] = array(
......@@ -425,3 +357,62 @@ public function getSettingsForm() {
return $form;
}
}
/**
* FileTransferException class.
*/
class FileTransferException extends Exception {
public $arguments;
function __construct($message, $code = 0, $arguments = array()) {
parent::__construct($message, $code);
$this->arguments = $arguments;
}
}
/**
* A FileTransfer Class implementing this interface can be used to chmod files.
*/
interface FileTransferChmodInterface {
/**
* Changes the permissions of the file / directory specified in $path
*
* @param string $path
* Path to change permissions of.
* @param long $mode
* @see http://php.net/chmod
* @param boolean $recursive
* Pass TRUE to recursively chmod the entire directory specified in $path.
*/
function chmodJailed($path, $mode, $recursive);
}
/**
* Provides an interface for iterating recursively over filesystem directories.
*
* Manually skips '.' and '..' directories, since no existing method is
* available in PHP 5.2.
*
* @todo Depreciate in favor of RecursiveDirectoryIterator::SKIP_DOTS once PHP
* 5.3 or later is required.
*/
class SkipDotsRecursiveDirectoryIterator extends RecursiveDirectoryIterator {
/**
* Constructs a SkipDotsRecursiveDirectoryIterator
*
* @param $path
* The path of the directory to be iterated over.
*/
function __construct($path) {
parent::__construct($path);
}
function next() {
parent::next();
while ($this->isDot()) {
parent::next();
}
}
}
<?php
/**
* @file
* Definition of Drupal\Core\FileTransfer\FTPExtension.
* Base class for FTP implementations.
*/
abstract class FileTransferFTP extends FileTransfer {
namespace Drupal\Core\FileTransfer;
public function __construct($jail, $username, $password, $hostname, $port) {
$this->username = $username;
$this->password = $password;
$this->hostname = $hostname;
$this->port = $port;
parent::__construct($jail);
}
/**
* Defines a file transfer class using the PHP FTP extension.
*/
class FTPExtension extends FTP implements ChmodInterface {
/**
* Return an object which can implement the FTP protocol.
*
* @param string $jail
* @param array $settings
* @return FileTransferFTP
* The appropriate FileTransferFTP subclass based on the available
* options. If the FTP PHP extension is available, use it.
*/
static function factory($jail, $settings) {
$username = empty($settings['username']) ? '' : $settings['username'];
$password = empty($settings['password']) ? '' : $settings['password'];
$hostname = empty($settings['advanced']['hostname']) ? 'localhost' : $settings['advanced']['hostname'];
$port = empty($settings['advanced']['port']) ? 21 : $settings['advanced']['port'];
if (function_exists('ftp_connect')) {
$class = 'FileTransferFTPExtension';
}
else {
throw new FileTransferException('No FTP backend available.');
}
return new $class($jail, $username, $password, $hostname, $port);
}
/**
* Implements Drupal\Core\FileTransfer\FileTransfer::connect().
* Returns the form to configure the FileTransfer class for FTP.
*/
public function getSettingsForm() {
$form = parent::getSettingsForm();
$form['advanced']['port']['#default_value'] = 21;
return $form;
}
}
class FileTransferFTPExtension extends FileTransferFTP implements FileTransferChmodInterface {
public function connect() {
$this->connection = ftp_connect($this->hostname, $this->port);
......@@ -26,27 +61,18 @@ public function connect() {
}
}
/**
* Implements Drupal\Core\FileTransfer\FileTransfer::copyFileJailed().
*/
protected function copyFileJailed($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));
}
}
/**
* Implements Drupal\Core\FileTransfer\FileTransfer::createDirectoryJailed().
*/
protected function createDirectoryJailed($directory) {
if (!ftp_mkdir($this->connection, $directory)) {
throw new FileTransferException("Cannot create directory @directory", NULL, array("@directory" => $directory));
}
}
/**
* Implements Drupal\Core\FileTransfer\FileTransfer::removeDirectoryJailed().
*/
protected function removeDirectoryJailed($directory) {
$pwd = ftp_pwd($this->connection);
if (!ftp_chdir($this->connection, $directory)) {
......@@ -74,18 +100,12 @@ protected function removeDirectoryJailed($directory) {
}
}
/**
* Implements Drupal\Core\FileTransfer\FileTransfer::removeFileJailed().
*/
protected function removeFileJailed($destination) {
if (!ftp_delete($this->connection, $destination)) {
throw new FileTransferException("Unable to remove to file @file", NULL, array('@file' => $destination));
}
}
/**
* Implements Drupal\Core\FileTransfer\FileTransfer::isDirectory().
*/
public function isDirectory($path) {
$result = FALSE;
$curr = ftp_pwd($this->connection);
......@@ -96,16 +116,10 @@ public function isDirectory($path) {
return $result;
}
/**
* Implements Drupal\Core\FileTransfer\FileTransfer::isFile().
*/
public function isFile($path) {
return ftp_size($this->connection, $path) != -1;
}
/**
* Implements Drupal\Core\FileTransfer\ChmodInterface::chmodJailed().
*/
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));
......@@ -122,3 +136,9 @@ function chmodJailed($path, $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));
}
}
<?php
/**
* @file
* Definition of Drupal\Core\FileTransfer\Local.
* The local connection class for copying files as the httpd user.
*/
class FileTransferLocal extends FileTransfer implements FileTransferChmodInterface {
namespace Drupal\Core\FileTransfer;
use RecursiveIteratorIterator;
use RecursiveDirectoryIterator;
/**
* Defines the local connection class for copying files as the httpd user.
*/
class Local extends FileTransfer implements ChmodInterface {
/**
* Implements Drupal\Core\FileTransfer\FileTransfer::connect().
*/
function connect() {
// No-op
}
/**
* Overrides Drupal\Core\FileTransfer\FileTransfer::factory().
*/
static function factory($jail, $settings) {
return new Local($jail);
return new FileTransferLocal($jail);
}
/**
* Implements Drupal\Core\FileTransfer\FileTransfer::copyFileJailed().
*/
protected function copyFileJailed($source, $destination) {
if (@!copy($source, $destination)) {
throw new FileTransferException('Cannot copy %source to %destination.', NULL, array('%source' => $source, '%destination' => $destination));