Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176
This commit is contained in:
commit
9921556621
13277 changed files with 1459781 additions and 0 deletions
29
core/lib/Drupal/Core/FileTransfer/ChmodInterface.php
Normal file
29
core/lib/Drupal/Core/FileTransfer/ChmodInterface.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \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
|
||||
* The new file permission mode to be passed to chmod().
|
||||
* @param bool $recursive
|
||||
* Pass TRUE to recursively chmod the entire directory specified in $path.
|
||||
*
|
||||
* @see http://php.net/chmod
|
||||
*/
|
||||
public function chmodJailed($path, $mode, $recursive);
|
||||
|
||||
}
|
53
core/lib/Drupal/Core/FileTransfer/FTP.php
Normal file
53
core/lib/Drupal/Core/FileTransfer/FTP.php
Normal file
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \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;
|
||||
}
|
||||
}
|
124
core/lib/Drupal/Core/FileTransfer/FTPExtension.php
Normal file
124
core/lib/Drupal/Core/FileTransfer/FTPExtension.php
Normal file
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\FileTransfer\FTPExtension.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\FileTransfer;
|
||||
|
||||
/**
|
||||
* Defines a file transfer class using the PHP FTP extension.
|
||||
*/
|
||||
class FTPExtension extends FTP implements ChmodInterface {
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\FileTransfer\FileTransfer::connect().
|
||||
*/
|
||||
public function connect() {
|
||||
$this->connection = ftp_connect($this->hostname, $this->port);
|
||||
|
||||
if (!$this->connection) {
|
||||
throw new FileTransferException("Cannot connect to FTP Server, check settings");
|
||||
}
|
||||
if (!ftp_login($this->connection, $this->username, $this->password)) {
|
||||
throw new FileTransferException("Cannot log in to FTP server. Check username and password");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)) {
|
||||
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;
|
||||
}
|
||||
if (@ftp_chdir($this->connection, $item)) {
|
||||
ftp_cdup($this->connection);
|
||||
$this->removeDirectory(ftp_pwd($this->connection) . '/' . $item);
|
||||
}
|
||||
else {
|
||||
$this->removeFile(ftp_pwd($this->connection) . '/' . $item);
|
||||
}
|
||||
}
|
||||
ftp_chdir($this->connection, $pwd);
|
||||
if (!ftp_rmdir($this->connection, $directory)) {
|
||||
throw new FileTransferException("Unable to remove to directory @directory", NULL, array('@directory' => $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);
|
||||
if (@ftp_chdir($this->connection, $path)) {
|
||||
$result = TRUE;
|
||||
}
|
||||
ftp_chdir($this->connection, $curr);
|
||||
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));
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
422
core/lib/Drupal/Core/FileTransfer/FileTransfer.php
Normal file
422
core/lib/Drupal/Core/FileTransfer/FileTransfer.php
Normal file
|
@ -0,0 +1,422 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\FileTransfer\FileTransfer.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\FileTransfer;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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. For
|
||||
* 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.
|
||||
*/
|
||||
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
|
||||
* be restricted to. This prevents the FileTransfer classes from being
|
||||
* able to touch other parts of the filesystem.
|
||||
* @param array $settings
|
||||
* 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.
|
||||
*
|
||||
* @param string $name
|
||||
* The name of the variable to return.
|
||||
*
|
||||
* @return string|bool
|
||||
* The variable specified in $name.
|
||||
*/
|
||||
function __get($name) {
|
||||
if ($name == 'connection') {
|
||||
$this->connect();
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
if ($name == 'chroot') {
|
||||
$this->setChroot();
|
||||
return $this->chroot;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to the server.
|
||||
*/
|
||||
abstract public function connect();
|
||||
|
||||
/**
|
||||
* Copies a directory.
|
||||
*
|
||||
* @param string $source
|
||||
* The source path.
|
||||
* @param string $destination
|
||||
* The destination path.
|
||||
*/
|
||||
public final function copyDirectory($source, $destination) {
|
||||
$source = $this->sanitizePath($source);
|
||||
$destination = $this->fixRemotePath($destination);
|
||||
$this->checkPath($destination);
|
||||
$this->copyDirectoryJailed($source, $destination);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the permissions of the specified $path (file or directory).
|
||||
*
|
||||
* @param string $path
|
||||
* The file / directory to change the permissions of.
|
||||
* @param int $mode
|
||||
* The new file permission mode to be passed to 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 (!($this instanceof ChmodInterface)) {
|
||||
throw new FileTransferException('Unable to change file permissions');
|
||||
}
|
||||
$path = $this->sanitizePath($path);
|
||||
$path = $this->fixRemotePath($path);
|
||||
$this->checkPath($path);
|
||||
$this->chmodJailed($path, $mode, $recursive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a directory.
|
||||
*
|
||||
* @param string $directory
|
||||
* The directory to be created.
|
||||
*/
|
||||
public final function createDirectory($directory) {
|
||||
$directory = $this->fixRemotePath($directory);
|
||||
$this->checkPath($directory);
|
||||
$this->createDirectoryJailed($directory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a directory.
|
||||
*
|
||||
* @param string $directory
|
||||
* The directory to be removed.
|
||||
*/
|
||||
public final function removeDirectory($directory) {
|
||||
$directory = $this->fixRemotePath($directory);
|
||||
$this->checkPath($directory);
|
||||
$this->removeDirectoryJailed($directory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a file.
|
||||
*
|
||||
* @param string $source
|
||||
* The source file.
|
||||
* @param string $destination
|
||||
* The destination file.
|
||||
*/
|
||||
public final function copyFile($source, $destination) {
|
||||
$source = $this->sanitizePath($source);
|
||||
$destination = $this->fixRemotePath($destination);
|
||||
$this->checkPath($destination);
|
||||
$this->copyFileJailed($source, $destination);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a file.
|
||||
*
|
||||
* @param string $destination
|
||||
* The destination file to be removed.
|
||||
*/
|
||||
public final function removeFile($destination) {
|
||||
$destination = $this->fixRemotePath($destination);
|
||||
$this->checkPath($destination);
|
||||
$this->removeFileJailed($destination);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the path is inside the jail and throws an exception if not.
|
||||
*
|
||||
* @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;
|
||||
$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 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);
|
||||
$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 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.
|
||||
if (substr($path, -1) == '/') {
|
||||
$path = substr($path, 0, -1);
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a directory.
|
||||
*
|
||||
* We need a separate method to make sure the $destination is in the jail.
|
||||
*
|
||||
* @param string $source
|
||||
* The source path.
|
||||
* @param string $destination
|
||||
* The destination path.
|
||||
*/
|
||||
protected function copyDirectoryJailed($source, $destination) {
|
||||
if ($this->isDirectory($destination)) {
|
||||
$destination = $destination . '/' . drupal_basename($source);
|
||||
}
|
||||
$this->createDirectory($destination);
|
||||
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);
|
||||
}
|
||||
else {
|
||||
$this->copyFile($file->getPathName(), $destination . $relative_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a directory.
|
||||
*
|
||||
* @param string $directory
|
||||
* The directory to be created.
|
||||
*/
|
||||
abstract protected function createDirectoryJailed($directory);
|
||||
|
||||
/**
|
||||
* Removes a directory.
|
||||
*
|
||||
* @param string $directory
|
||||
* The directory to be removed.
|
||||
*/
|
||||
abstract protected function removeDirectoryJailed($directory);
|
||||
|
||||
/**
|
||||
* Copies a file.
|
||||
*
|
||||
* @param string $source
|
||||
* The source file.
|
||||
* @param string $destination
|
||||
* The destination file.
|
||||
*/
|
||||
abstract protected function copyFileJailed($source, $destination);
|
||||
|
||||
/**
|
||||
* Removes a file.
|
||||
*
|
||||
* @param string $destination
|
||||
* The destination file to be removed.
|
||||
*/
|
||||
abstract protected function removeFileJailed($destination);
|
||||
|
||||
/**
|
||||
* Checks if a particular path is a directory.
|
||||
*
|
||||
* @param string $path
|
||||
* The path to check
|
||||
*
|
||||
* @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 string $path
|
||||
* The path to check.
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @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.
|
||||
$path = __FILE__;
|
||||
$path = $this->fixRemotePath($path, FALSE);
|
||||
if ($this->isFile($path)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$path = __DIR__;
|
||||
$path = $this->fixRemotePath($path, FALSE);
|
||||
$parts = explode('/', $path);
|
||||
$chroot = '';
|
||||
while (count($parts)) {
|
||||
$check = implode($parts, '/');
|
||||
if ($this->isFile($check . '/' . drupal_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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a form to collect connection settings credentials.
|
||||
*
|
||||
* 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(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Username'),
|
||||
);
|
||||
$form['password'] = array(
|
||||
'#type' => 'password',
|
||||
'#title' => t('Password'),
|
||||
'#description' => t('Your password is not saved in the database and is only used to establish a connection.'),
|
||||
);
|
||||
$form['advanced'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => t('Advanced settings'),
|
||||
);
|
||||
$form['advanced']['hostname'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Host'),
|
||||
'#default_value' => 'localhost',
|
||||
'#description' => t('The connection will be created between your web server and the machine hosting the web server files. In the vast majority of cases, this will be the same machine, and "localhost" is correct.'),
|
||||
);
|
||||
$form['advanced']['port'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Port'),
|
||||
'#default_value' => NULL,
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
}
|
36
core/lib/Drupal/Core/FileTransfer/FileTransferException.php
Normal file
36
core/lib/Drupal/Core/FileTransfer/FileTransferException.php
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\FileTransfer\FileTransferException.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\FileTransfer;
|
||||
|
||||
/**
|
||||
* FileTransferException class.
|
||||
*/
|
||||
class FileTransferException extends \RuntimeException {
|
||||
|
||||
/**
|
||||
* Arguments to be used in this exception.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $arguments;
|
||||
|
||||
/**
|
||||
* Constructs a FileTransferException object.
|
||||
*
|
||||
* @param string $message
|
||||
* Exception message.
|
||||
* @param int $code
|
||||
* Exception code.
|
||||
* @param array $arguments
|
||||
* Arguments to be used in this exception.
|
||||
*/
|
||||
function __construct($message, $code = 0, $arguments = array()) {
|
||||
parent::__construct($message, $code);
|
||||
$this->arguments = $arguments;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,345 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\FileTransfer\Form\FileTransferAuthorizeForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\FileTransfer\Form;
|
||||
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides the file transfer authorization form.
|
||||
*/
|
||||
class FileTransferAuthorizeForm extends FormBase {
|
||||
|
||||
/**
|
||||
* The app root.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $root;
|
||||
|
||||
/**
|
||||
* Constructs a new FileTransferAuthorizeForm object.
|
||||
*
|
||||
* @param string $root
|
||||
* The app root.
|
||||
*/
|
||||
public function __construct($root) {
|
||||
$this->root = $root;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static ($container->get('app.root'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'authorize_filetransfer_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
// Get all the available ways to transfer files.
|
||||
if (empty($_SESSION['authorize_filetransfer_info'])) {
|
||||
drupal_set_message($this->t('Unable to continue, no available methods of file transfer'), 'error');
|
||||
return array();
|
||||
}
|
||||
$available_backends = $_SESSION['authorize_filetransfer_info'];
|
||||
|
||||
if (!$this->getRequest()->isSecure()) {
|
||||
$form['information']['https_warning'] = array(
|
||||
'#prefix' => '<div class="messages messages--error">',
|
||||
'#markup' => $this->t('WARNING: You are not using an encrypted connection, so your password will be sent in plain text. <a href="@https-link">Learn more</a>.', array('@https-link' => 'https://www.drupal.org/https-information')),
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
}
|
||||
|
||||
// Decide on a default backend.
|
||||
if ($authorize_filetransfer_default = $form_state->getValue(array('connection_settings', 'authorize_filetransfer_default')));
|
||||
elseif ($authorize_filetransfer_default = $this->config('system.authorize')->get('filetransfer_default'));
|
||||
else {
|
||||
$authorize_filetransfer_default = key($available_backends);
|
||||
}
|
||||
|
||||
$form['information']['main_header'] = array(
|
||||
'#prefix' => '<h3>',
|
||||
'#markup' => $this->t('To continue, provide your server connection details'),
|
||||
'#suffix' => '</h3>',
|
||||
);
|
||||
|
||||
$form['connection_settings']['#tree'] = TRUE;
|
||||
$form['connection_settings']['authorize_filetransfer_default'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Connection method'),
|
||||
'#default_value' => $authorize_filetransfer_default,
|
||||
'#weight' => -10,
|
||||
);
|
||||
|
||||
/*
|
||||
* Here we create two submit buttons. For a JS enabled client, they will
|
||||
* only ever see submit_process. However, if a client doesn't have JS
|
||||
* enabled, they will see submit_connection on the first form (when picking
|
||||
* what filetransfer type to use, and submit_process on the second one (which
|
||||
* leads to the actual operation).
|
||||
*/
|
||||
$form['submit_connection'] = array(
|
||||
'#prefix' => "<br style='clear:both'/>",
|
||||
'#name' => 'enter_connection_settings',
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Enter connection settings'),
|
||||
'#weight' => 100,
|
||||
);
|
||||
|
||||
$form['submit_process'] = array(
|
||||
'#name' => 'process_updates',
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Continue'),
|
||||
'#weight' => 100,
|
||||
);
|
||||
|
||||
// Build a container for each connection type.
|
||||
foreach ($available_backends as $name => $backend) {
|
||||
$form['connection_settings']['authorize_filetransfer_default']['#options'][$name] = $backend['title'];
|
||||
$form['connection_settings'][$name] = array(
|
||||
'#type' => 'container',
|
||||
'#attributes' => array('class' => array("filetransfer-$name", 'filetransfer')),
|
||||
'#states' => array(
|
||||
'visible' => array(
|
||||
'select[name="connection_settings[authorize_filetransfer_default]"]' => array('value' => $name),
|
||||
),
|
||||
),
|
||||
);
|
||||
// We can't use #prefix on the container itself since then the header won't
|
||||
// be hidden and shown when the containers are being manipulated via JS.
|
||||
$form['connection_settings'][$name]['header'] = array(
|
||||
'#markup' => '<h4>' . $this->t('@backend connection settings', array('@backend' => $backend['title'])) . '</h4>',
|
||||
);
|
||||
|
||||
$form['connection_settings'][$name] += $this->addConnectionSettings($name);
|
||||
|
||||
// Start non-JS code.
|
||||
if ($form_state->getValue(array('connection_settings', 'authorize_filetransfer_default')) == $name) {
|
||||
|
||||
// Change the submit button to the submit_process one.
|
||||
$form['submit_process']['#attributes'] = array();
|
||||
unset($form['submit_connection']);
|
||||
|
||||
// Activate the proper filetransfer settings form.
|
||||
$form['connection_settings'][$name]['#attributes']['style'] = 'display:block';
|
||||
// Disable the select box.
|
||||
$form['connection_settings']['authorize_filetransfer_default']['#disabled'] = TRUE;
|
||||
|
||||
// Create a button for changing the type of connection.
|
||||
$form['connection_settings']['change_connection_type'] = array(
|
||||
'#name' => 'change_connection_type',
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Change connection type'),
|
||||
'#weight' => -5,
|
||||
'#attributes' => array('class' => array('filetransfer-change-connection-type')),
|
||||
);
|
||||
}
|
||||
// End non-JS code.
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
// Only validate the form if we have collected all of the user input and are
|
||||
// ready to proceed with updating or installing.
|
||||
if ($form_state->getTriggeringElement()['#name'] != 'process_updates') {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($form_connection_settings = $form_state->getValue('connection_settings')) {
|
||||
$backend = $form_connection_settings['authorize_filetransfer_default'];
|
||||
$filetransfer = $this->getFiletransfer($backend, $form_connection_settings[$backend]);
|
||||
try {
|
||||
if (!$filetransfer) {
|
||||
throw new \Exception($this->t('The connection protocol %backend does not exist.', array('%backend' => $backend)));
|
||||
}
|
||||
$filetransfer->connect();
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// The format of this error message is similar to that used on the
|
||||
// database connection form in the installer.
|
||||
$form_state->setErrorByName('connection_settings', $this->t('Failed to connect to the server. The server reports the following message: !message For more help installing or updating code on your server, see the <a href="@handbook_url">handbook</a>.', array(
|
||||
'!message' => '<p class="error">' . $e->getMessage() . '</p>',
|
||||
'@handbook_url' => 'https://www.drupal.org/documentation/install/modules-themes',
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$form_connection_settings = $form_state->getValue('connection_settings');
|
||||
switch ($form_state->getTriggeringElement()['#name']) {
|
||||
case 'process_updates':
|
||||
|
||||
// Save the connection settings to the DB.
|
||||
$filetransfer_backend = $form_connection_settings['authorize_filetransfer_default'];
|
||||
|
||||
// If the database is available then try to save our settings. We have
|
||||
// to make sure it is available since this code could potentially (will
|
||||
// likely) be called during the installation process, before the
|
||||
// database is set up.
|
||||
try {
|
||||
$connection_settings = array();
|
||||
foreach ($form_connection_settings[$filetransfer_backend] as $key => $value) {
|
||||
// We do *not* want to store passwords in the database, unless the
|
||||
// backend explicitly says so via the magic #filetransfer_save form
|
||||
// property. Otherwise, we store everything that's not explicitly
|
||||
// marked with #filetransfer_save set to FALSE.
|
||||
if (!isset($form['connection_settings'][$filetransfer_backend][$key]['#filetransfer_save'])) {
|
||||
if ($form['connection_settings'][$filetransfer_backend][$key]['#type'] != 'password') {
|
||||
$connection_settings[$key] = $value;
|
||||
}
|
||||
}
|
||||
// The attribute is defined, so only save if set to TRUE.
|
||||
elseif ($form['connection_settings'][$filetransfer_backend][$key]['#filetransfer_save']) {
|
||||
$connection_settings[$key] = $value;
|
||||
}
|
||||
}
|
||||
// Set this one as the default authorize method.
|
||||
$this->config('system.authorize')->set('filetransfer_default', $filetransfer_backend);
|
||||
// Save the connection settings minus the password.
|
||||
$this->config('system.authorize')->set('filetransfer_connection_settings_' . $filetransfer_backend, $connection_settings);
|
||||
|
||||
$filetransfer = $this->getFiletransfer($filetransfer_backend, $form_connection_settings[$filetransfer_backend]);
|
||||
|
||||
// Now run the operation.
|
||||
$this->runOperation($filetransfer);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// If there is no database available, we don't care and just skip
|
||||
// this part entirely.
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'enter_connection_settings':
|
||||
$form_state->setRebuild();
|
||||
break;
|
||||
|
||||
case 'change_connection_type':
|
||||
$form_state->setRebuild();
|
||||
$form_state->unsetValue(array('connection_settings', 'authorize_filetransfer_default'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a FileTransfer class for a specific transfer method and settings.
|
||||
*
|
||||
* @param $backend
|
||||
* The FileTransfer backend to get the class for.
|
||||
* @param $settings
|
||||
* Array of settings for the FileTransfer.
|
||||
*
|
||||
* @return \Drupal\Core\FileTransfer\FileTransfer|bool
|
||||
* An instantiated FileTransfer object for the requested method and settings,
|
||||
* or FALSE if there was an error finding or instantiating it.
|
||||
*/
|
||||
protected function getFiletransfer($backend, $settings = array()) {
|
||||
$filetransfer = FALSE;
|
||||
if (!empty($_SESSION['authorize_filetransfer_info'][$backend])) {
|
||||
$backend_info = $_SESSION['authorize_filetransfer_info'][$backend];
|
||||
if (class_exists($backend_info['class'])) {
|
||||
$filetransfer = $backend_info['class']::factory($this->root, $settings);
|
||||
}
|
||||
}
|
||||
return $filetransfer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the Form API array for a given connection backend's settings.
|
||||
*
|
||||
* @param string $backend
|
||||
* The name of the backend (e.g. 'ftp', 'ssh', etc).
|
||||
*
|
||||
* @return array
|
||||
* Form API array of connection settings for the given backend.
|
||||
*
|
||||
* @see hook_filetransfer_backends()
|
||||
*/
|
||||
protected function addConnectionSettings($backend) {
|
||||
$auth_connection_config = $this->config('system.authorize')->get('filetransfer_connection_settings_' . $backend);
|
||||
$defaults = $auth_connection_config ? $auth_connection_config : array();
|
||||
$form = array();
|
||||
|
||||
// Create an instance of the file transfer class to get its settings form.
|
||||
$filetransfer = $this->getFiletransfer($backend);
|
||||
if ($filetransfer) {
|
||||
$form = $filetransfer->getSettingsForm();
|
||||
}
|
||||
// Fill in the defaults based on the saved settings, if any.
|
||||
$this->setConnectionSettingsDefaults($form, NULL, $defaults);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default settings on a file transfer connection form recursively.
|
||||
*
|
||||
* The default settings for the file transfer connection forms are saved in
|
||||
* the database. The settings are stored as a nested array in the case of a
|
||||
* settings form that has details or otherwise uses a nested structure.
|
||||
* Therefore, to properly add defaults, we need to walk through all the
|
||||
* children form elements and process those defaults recursively.
|
||||
*
|
||||
* @param $element
|
||||
* Reference to the Form API form element we're operating on.
|
||||
* @param $key
|
||||
* The key for our current form element, if any.
|
||||
* @param array $defaults
|
||||
* The default settings for the file transfer backend we're operating on.
|
||||
*/
|
||||
protected function setConnectionSettingsDefaults(&$element, $key, array $defaults) {
|
||||
// If we're operating on a form element which isn't a details, and we have
|
||||
// a default setting saved, stash it in #default_value.
|
||||
if (!empty($key) && isset($defaults[$key]) && isset($element['#type']) && $element['#type'] != 'details') {
|
||||
$element['#default_value'] = $defaults[$key];
|
||||
}
|
||||
// Now, we walk through all the child elements, and recursively invoke
|
||||
// ourselves on each one. Since the $defaults settings array can be nested
|
||||
// (because of #tree, any values inside details will be nested), if
|
||||
// there's a subarray of settings for the form key we're currently
|
||||
// processing, pass in that subarray to the recursive call. Otherwise, just
|
||||
// pass on the whole $defaults array.
|
||||
foreach (Element::children($element) as $child_key) {
|
||||
$this->setConnectionSettingsDefaults($element[$child_key], $child_key, ((isset($defaults[$key]) && is_array($defaults[$key])) ? $defaults[$key] : $defaults));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the operation specified in $_SESSION['authorize_operation'].
|
||||
*
|
||||
* @param $filetransfer
|
||||
* The FileTransfer object to use for running the operation.
|
||||
*/
|
||||
protected function runOperation($filetransfer) {
|
||||
$operation = $_SESSION['authorize_operation'];
|
||||
unset($_SESSION['authorize_operation']);
|
||||
|
||||
require_once $this->root . '/' . $operation['file'];
|
||||
call_user_func_array($operation['callback'], array_merge(array($filetransfer), $operation['arguments']));
|
||||
}
|
||||
|
||||
}
|
110
core/lib/Drupal/Core/FileTransfer/Local.php
Normal file
110
core/lib/Drupal/Core/FileTransfer/Local.php
Normal file
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\FileTransfer\Local.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\FileTransfer;
|
||||
|
||||
/**
|
||||
* Defines the local connection class for copying files as the httpd user.
|
||||
*/
|
||||
class Local extends FileTransfer implements ChmodInterface {
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\FileTransfer\FileTransfer::connect().
|
||||
*/
|
||||
public function connect() {
|
||||
// No-op
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\FileTransfer\FileTransfer::factory().
|
||||
*/
|
||||
static function factory($jail, $settings) {
|
||||
return new Local($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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\FileTransfer\FileTransfer::createDirectoryJailed().
|
||||
*/
|
||||
protected function createDirectoryJailed($directory) {
|
||||
if (!is_dir($directory) && @!mkdir($directory, 0777, TRUE)) {
|
||||
throw new FileTransferException('Cannot create directory %directory.', NULL, array('%directory' => $directory));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\FileTransfer\FileTransfer::removeDirectoryJailed().
|
||||
*/
|
||||
protected function removeDirectoryJailed($directory) {
|
||||
if (!is_dir($directory)) {
|
||||
// Programmer error assertion, not something we expect users to see.
|
||||
throw new FileTransferException('removeDirectoryJailed() called with a path (%directory) that is not a directory.', NULL, array('%directory' => $directory));
|
||||
}
|
||||
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::CHILD_FIRST) as $filename => $file) {
|
||||
if ($file->isDir()) {
|
||||
if (@!drupal_rmdir($filename)) {
|
||||
throw new FileTransferException('Cannot remove directory %directory.', NULL, array('%directory' => $filename));
|
||||
}
|
||||
}
|
||||
elseif ($file->isFile()) {
|
||||
if (@!drupal_unlink($filename)) {
|
||||
throw new FileTransferException('Cannot remove file %file.', NULL, array('%file' => $filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (@!drupal_rmdir($directory)) {
|
||||
throw new FileTransferException('Cannot remove directory %directory.', NULL, array('%directory' => $directory));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\FileTransfer\FileTransfer::removeFileJailed().
|
||||
*/
|
||||
protected function removeFileJailed($file) {
|
||||
if (@!drupal_unlink($file)) {
|
||||
throw new FileTransferException('Cannot remove file %file.', NULL, array('%file' => $file));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\FileTransfer\FileTransfer::isDirectory().
|
||||
*/
|
||||
public function isDirectory($path) {
|
||||
return is_dir($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\FileTransfer\FileTransfer::isFile().
|
||||
*/
|
||||
public function isFile($path) {
|
||||
return is_file($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\FileTransfer\ChmodInterface::chmodJailed().
|
||||
*/
|
||||
public function chmodJailed($path, $mode, $recursive) {
|
||||
if ($recursive && is_dir($path)) {
|
||||
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST) as $filename => $file) {
|
||||
if (@!chmod($filename, $mode)) {
|
||||
throw new FileTransferException('Cannot chmod %path.', NULL, array('%path' => $filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif (@!chmod($path, $mode)) {
|
||||
throw new FileTransferException('Cannot chmod %path.', NULL, array('%path' => $path));
|
||||
}
|
||||
}
|
||||
}
|
150
core/lib/Drupal/Core/FileTransfer/SSH.php
Normal file
150
core/lib/Drupal/Core/FileTransfer/SSH.php
Normal file
|
@ -0,0 +1,150 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\FileTransfer\SSH.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\FileTransfer;
|
||||
|
||||
/**
|
||||
* The SSH connection class for the update module.
|
||||
*/
|
||||
class SSH extends FileTransfer implements ChmodInterface {
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\FileTransfer\FileTransfer::__construct().
|
||||
*/
|
||||
function __construct($jail, $username, $password, $hostname = "localhost", $port = 22) {
|
||||
$this->username = $username;
|
||||
$this->password = $password;
|
||||
$this->hostname = $hostname;
|
||||
$this->port = $port;
|
||||
parent::__construct($jail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\FileTransfer\FileTransfer::connect().
|
||||
*/
|
||||
public function connect() {
|
||||
$this->connection = @ssh2_connect($this->hostname, $this->port);
|
||||
if (!$this->connection) {
|
||||
throw new FileTransferException('SSH Connection failed to @host:@port', NULL, array('@host' => $this->hostname, '@port' => $this->port));
|
||||
}
|
||||
if (!@ssh2_auth_password($this->connection, $this->username, $this->password)) {
|
||||
throw new FileTransferException('The supplied username/password combination was not accepted.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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']) ? 22 : $settings['advanced']['port'];
|
||||
return new SSH($jail, $username, $password, $hostname, $port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\FileTransfer\FileTransfer::copyFileJailed().
|
||||
*/
|
||||
protected function copyFileJailed($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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\FileTransfer\FileTransfer::copyDirectoryJailed().
|
||||
*/
|
||||
protected function copyDirectoryJailed($source, $destination) {
|
||||
if (@!ssh2_exec($this->connection, 'cp -Rp ' . escapeshellarg($source) . ' ' . escapeshellarg($destination))) {
|
||||
throw new FileTransferException('Cannot copy directory @directory.', NULL, array('@directory' => $source));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\FileTransfer\FileTransfer::createDirectoryJailed().
|
||||
*/
|
||||
protected function createDirectoryJailed($directory) {
|
||||
if (@!ssh2_exec($this->connection, 'mkdir ' . escapeshellarg($directory))) {
|
||||
throw new FileTransferException('Cannot create directory @directory.', NULL, array('@directory' => $directory));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\FileTransfer\FileTransfer::removeDirectoryJailed().
|
||||
*/
|
||||
protected function removeDirectoryJailed($directory) {
|
||||
if (@!ssh2_exec($this->connection, 'rm -Rf ' . escapeshellarg($directory))) {
|
||||
throw new FileTransferException('Cannot remove @directory.', NULL, array('@directory' => $directory));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\FileTransfer\FileTransfer::removeFileJailed().
|
||||
*/
|
||||
protected function removeFileJailed($destination) {
|
||||
if (!@ssh2_exec($this->connection, 'rm ' . escapeshellarg($destination))) {
|
||||
throw new FileTransferException('Cannot remove @directory.', NULL, array('@directory' => $destination));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\FileTransfer\FileTransfer::isDirectory().
|
||||
*
|
||||
* WARNING: This is untested. It is not currently used, but should do the
|
||||
* trick.
|
||||
*/
|
||||
public function isDirectory($path) {
|
||||
$directory = escapeshellarg($path);
|
||||
$cmd = "[ -d {$directory} ] && 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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\FileTransfer\FileTransfer::isFile().
|
||||
*/
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\FileTransfer\ChmodInterface::chmodJailed().
|
||||
*/
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\FileTransfer\FileTransfer::getSettingsForm().
|
||||
*/
|
||||
public function getSettingsForm() {
|
||||
$form = parent::getSettingsForm();
|
||||
$form['advanced']['port']['#default_value'] = 22;
|
||||
return $form;
|
||||
}
|
||||
}
|
Reference in a new issue