Commit 837430ff authored by catch's avatar catch
Browse files

Issue #1323124 by amateescu, aspilicious, Berdir: Convert file transfer system...

Issue #1323124 by amateescu, aspilicious, Berdir: Convert file transfer system to PSR-0. Commit ALL THE files.
parent e89ce282
......@@ -320,10 +320,6 @@ 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,28 +7535,14 @@ function drupal_get_updaters() {
* @return
* The Drupal FileTransfer class registry.
*
* @see FileTransfer
* @see Drupal\Core\FileTransfer\FileTransfer
* @see hook_filetransfer_info()
* @see hook_filetransfer_info_alter()
*/
function drupal_get_filetransfer_info() {
$info = &drupal_static(__FUNCTION__);
if (!isset($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);
}
}
$info = module_invoke_all('filetransfer_info');
drupal_alter('filetransfer_info', $info);
uasort($info, 'drupal_sort_weight');
}
......
<?php
/**
* @file
* Definition of Drupal\Core\FileTransfer\ChmodInterface.
*/
namespace Drupal\Core\FileTransfer;
/**
* Defines an interface to chmod files.
*/
interface ChmodInterface {
/**
* Changes the permissions of the file / directory specified in $path
*
* @param string $path
* Path to change permissions of.
* @param int $mode
* See the $mode argument from http://php.net/chmod.
* @param bool $recursive
* Pass TRUE to recursively chmod the entire directory specified in $path.
*
* @see http://php.net/chmod
*/
function chmodJailed($path, $mode, $recursive);
}
<?php
/**
* @file
* Definition of Drupal\Core\FileTransfer\FTP.
*/
namespace Drupal\Core\FileTransfer;
/**
* Defines the base class for FTP implementations.
*/
abstract class FTP extends FileTransfer {
/**
* Overrides Drupal\Core\FileTransfer\FileTransfer::__construct().
*/
public function __construct($jail, $username, $password, $hostname, $port) {
$this->username = $username;
$this->password = $password;
$this->hostname = $hostname;
$this->port = $port;
parent::__construct($jail);
}
/**
* Overrides Drupal\Core\FileTransfer\FileTransfer::factory().
*/
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 = 'Drupal\Core\FileTransfer\FTPExtension';
}
else {
throw new FileTransferException('No FTP backend available.');
}
return new $class($jail, $username, $password, $hostname, $port);
}
/**
* Overrides Drupal\Core\FileTransfer\FileTransfer::getSettingsForm().
*/
public function getSettingsForm() {
$form = parent::getSettingsForm();
$form['advanced']['port']['#default_value'] = 21;
return $form;
}
}
<?php
/**
* Base class for FTP implementations.
* @file
* Definition of Drupal\Core\FileTransfer\FTPExtension.
*/
abstract class FileTransferFTP 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);
}
/**
* 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'];
namespace Drupal\Core\FileTransfer;
if (function_exists('ftp_connect')) {
$class = 'FileTransferFTPExtension';
}
else {
throw new FileTransferException('No FTP backend available.');
}
return new $class($jail, $username, $password, $hostname, $port);
}
/**
* Defines a file transfer class using the PHP FTP extension.
*/
class FTPExtension extends FTP implements ChmodInterface {
/**
* Returns the form to configure the FileTransfer class for FTP.
* Implements Drupal\Core\FileTransfer\FileTransfer::connect().
*/
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);
......@@ -61,18 +26,27 @@ 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)) {
......@@ -100,12 +74,18 @@ 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);
......@@ -116,10 +96,16 @@ 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));
......@@ -136,9 +122,3 @@ 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));
}
}
......@@ -2,7 +2,16 @@
/**
* @file
* Base FileTransfer class.
* Definition of Drupal\Core\FileTransfer\FileTransfer.
*/
namespace Drupal\Core\FileTransfer;
use RecursiveIteratorIterator;
use RecursiveDirectoryIterator;
/**
* Defines the 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
......@@ -11,21 +20,52 @@
* 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;
/**
* The constructor for the UpdateConnection class. This method is also called
* from the classes that extend this class and override this method.
* 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.
*/
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
......@@ -35,19 +75,28 @@ 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.');
}
/**
* Implementation of the magic __get() method.
* 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.
*
* 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.
* @param string $name
* The name of the variable to return.
*
* @return string|bool
* The variable specified in $name.
*/
function __get($name) {
if ($name == 'connection') {
......@@ -69,9 +118,9 @@ abstract protected function connect();
/**
* Copies a directory.
*
* @param $source
* @param string $source
* The source path.
* @param $destination
* @param string $destination
* The destination path.
*/
public final function copyDirectory($source, $destination) {
......@@ -82,14 +131,21 @@ public final function copyDirectory($source, $destination) {
}
/**
* @see http://php.net/chmod
* Changes the permissions of the specified $path (file or directory).
*
* @param string $path
* @param long $mode
* The file / directory to change the permissions of.
* @param int $mode
* See the $mode argument from http://php.net/chmod.
* @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('FileTransferChmodInterface', class_implements(get_class($this)))) {
if (!in_array('Drupal\Core\FileTransfer\ChmodInterface', class_implements(get_class($this)))) {
throw new FileTransferException('Unable to change file permissions');
}
$path = $this->sanitizePath($path);
......@@ -101,7 +157,7 @@ public final function chmod($path, $mode, $recursive = FALSE) {
/**
* Creates a directory.
*
* @param $directory
* @param string $directory
* The directory to be created.
*/
public final function createDirectory($directory) {
......@@ -113,7 +169,7 @@ public final function createDirectory($directory) {
/**
* Removes a directory.
*
* @param $directory
* @param string $directory
* The directory to be removed.
*/
public final function removeDirectory($directory) {
......@@ -125,9 +181,9 @@ public final function removeDirectory($directory) {
/**
* Copies a file.
*
* @param $source
* @param string $source
* The source file.
* @param $destination
* @param string $destination
* The destination file.
*/
public final function copyFile($source, $destination) {
......@@ -140,7 +196,7 @@ public final function copyFile($source, $destination) {
/**
* Removes a file.
*
* @param $destination
* @param string $destination
* The destination file to be removed.
*/
public final function removeFile($destination) {
......@@ -152,8 +208,10 @@ public final function removeFile($destination) {
/**
* Checks that the path is inside the jail and throws an exception if not.
*
* @param $path
* @param string $path
* A path to check against the jail.
*
* @throws Drupal\Core\FileTransfer\FileTransferException
*/
protected final function checkPath($path) {
$full_jail = $this->chroot . $this->jail;
......@@ -166,14 +224,18 @@ 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.
*
* @param $path
* @param $strip_chroot
* 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.
*
* @return string
* The modified path.
*/
protected final function fixRemotePath($path, $strip_chroot = TRUE) {
$path = $this->sanitizePath($path);
......@@ -190,7 +252,10 @@ 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.
......@@ -203,11 +268,11 @@ function sanitizePath($path) {
/**
* Copies a directory.
*
* We need a separate method to make the $destination is in the jail.
* We need a separate method to make sure the $destination is in the jail.
*
* @param $source
* @param string $source
* The source path.
* @param $destination
* @param string $destination
* The destination path.
*/
protected function copyDirectoryJailed($source, $destination) {
......@@ -215,7 +280,7 @@ protected function copyDirectoryJailed($source, $destination) {
$destination = $destination . '/' . drupal_basename($source);
}
$this->createDirectory($destination);
foreach (new RecursiveIteratorIterator(new SkipDotsRecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST) as $filename => $file) {
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST) as $filename => $file) {
$relative_path = substr($filename, strlen($source));
if ($file->isDir()) {
$this->createDirectory($destination . $relative_path);
......@@ -229,7 +294,7 @@ protected function copyDirectoryJailed($source, $destination) {
/**
* Creates a directory.
*
* @param $directory
* @param string $directory
* The directory to be created.
*/
abstract protected function createDirectoryJailed($directory);
......@@ -237,7 +302,7 @@ abstract protected function createDirectoryJailed($directory);
/**
* Removes a directory.
*
* @param $directory
* @param string $directory
* The directory to be removed.
*/
abstract protected function removeDirectoryJailed($directory);
......@@ -245,9 +310,9 @@ abstract protected function removeDirectoryJailed($directory);
/**
* Copies a file.
*
* @param $source
* @param string $source
* The source file.
* @param $destination
* @param string $destination
* The destination file.
*/
abstract protected function copyFileJailed($source, $destination);
......@@ -255,39 +320,40 @@ abstract protected function copyFileJailed($source, $destination);
/**
* Removes a file.
*
* @param $destination
* @param string $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 $path
* @param string $path
* The path to check
*
* @return boolean
* @return bool
* TRUE if the specified path is a directory, FALSE otherwise.
*/
abstract public function isDirectory($path);
/**
* Checks if a particular path is a file (not a directory).
*
* @param $path
* The path to check
* @param string $path
* The path to check.
*
* @return boolean
* @return bool
* TRUE if the specified path is a file, FALSE otherwise.
*/
abstract public function isFile($path);
/**
* Returns the chroot property for this connection.
*
* It does this by moving up the tree until it finds itself. If successful,
* it will return the chroot, otherwise FALSE.
* It does this by moving up the tree until it finds itself
*
* @return
* The chroot path for this connection or FALSE.
* @return string|bool
* If successful, the chroot path for this connection, otherwise FALSE.
*/
function findChroot() {
// If the file exists as is, there is no chroot.
......@@ -313,8 +379,7 @@ 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();
......@@ -326,6 +391,9 @@ 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(
......@@ -357,62 +425,3 @@ 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;
}