diff --git a/core/src/core/classes/class.AJXP_Utils.php b/core/src/core/classes/class.AJXP_Utils.php index 73d067801c..fa861c9adb 100644 --- a/core/src/core/classes/class.AJXP_Utils.php +++ b/core/src/core/classes/class.AJXP_Utils.php @@ -1616,6 +1616,19 @@ public static function safeParseUrl($path) return $parts; } + /** + * Sanitize a URL removin all unwanted / trailing slashes + * + * @param array url_parts + * return string url + */ + public function getSanitizedUrl($arr) { + $credentials = join(':', array_filter([$arr['user'], $arr['pass']])); + $hostname = join(':', array_filter([$arr['host'], $arr['port']])); + $domain = join('@', array_filter([$credentials, $hostname])); + return $arr['scheme'] . '://' . join('/', array_filter([$domain, $arr['path']])); + } + /** * @static * @param string $url @@ -2092,5 +2105,5 @@ public static function regexpToLdap($regexp) public static function winSetHidden($file) { @shell_exec("attrib +H " . escapeshellarg($file)); - } + } } diff --git a/core/src/plugins/access.webdav/composer.json b/core/src/plugins/access.webdav/composer.json index 6bf8e111d4..1328aa5c45 100644 --- a/core/src/plugins/access.webdav/composer.json +++ b/core/src/plugins/access.webdav/composer.json @@ -4,8 +4,8 @@ "homepage": "http://pydio.com/", "autoload": { "psr-4": { - "CoreAccess\\": "../core.access/src", - "CoreAccess\\Stream\\Client\\": "./src/" + "Pydio\\Access\\Core\\": "../core.access/src", + "Pydio\\Access\\WebDAV\\": "./src/" } } } diff --git a/core/src/plugins/access.webdav/manifest.xml b/core/src/plugins/access.webdav/manifest.xml index 39ae6cd29b..1c0ee96c24 100644 --- a/core/src/plugins/access.webdav/manifest.xml +++ b/core/src/plugins/access.webdav/manifest.xml @@ -1,6 +1,6 @@ - + @@ -22,5 +22,5 @@ - + diff --git a/core/src/plugins/access.webdav/src/DAVClient.php b/core/src/plugins/access.webdav/src/Client.php similarity index 92% rename from core/src/plugins/access.webdav/src/DAVClient.php rename to core/src/plugins/access.webdav/src/Client.php index 81f2478a5b..4b4aba51eb 100755 --- a/core/src/plugins/access.webdav/src/DAVClient.php +++ b/core/src/plugins/access.webdav/src/Client.php @@ -14,14 +14,16 @@ * permissions and limitations under the License. */ -namespace CoreAccess\Stream\Client; +namespace Pydio\Access\WebDAV; require_once(__DIR__ . '/../../../core/classes/guzzle/vendor/autoload.php'); require_once(__DIR__ . '/../../../core/classes/sabredav/lib/Sabre/autoload.php'); -use CoreAccess\Stream\StreamWrapper; -use CoreAccess\Stream\Listener\PathListener; -use CoreAccess\Stream\Iterator\DirIterator; + +use Pydio\Access\Core\Stream\Client as CoreClient; +use Pydio\Access\Core\Stream\Listener\PathListener; +use Pydio\Access\Core\Stream\Iterator\DirIterator; +use Pydio\Access\Core\Stream\StreamWrapper; use Guzzle\Service\Loader\JsonLoader; use GuzzleHttp\Client as GuzzleClient; use GuzzleHttp\Command\Guzzle\Description as GuzzleDescription; @@ -33,9 +35,9 @@ * Client to interact with a WebDAV FS * */ -class DAVClient extends AbstractClient +class Client extends CoreClient { - const PROTOCOL = "dav"; + const PROTOCOL = "pydio.dav"; const RESOURCES_PATH = "Resources"; const RESOURCES_FILE = "dav.json"; @@ -74,7 +76,7 @@ public function __construct($config = array()) */ public function registerStreamWrapper() { - StreamWrapper::register($this); + StreamWrapper::register($this, self::PROTOCOL); return $this; } @@ -113,8 +115,8 @@ private function _formatUrlStat($arr) { private function _getSabreDAVClient($params) { return new SabreDAVClient([ 'baseUri' => $this->urlParams["scheme"] . "://" . $this->urlParams['host'], - 'userName' => $params['auth'][0], - 'password' => $params['auth'][1] + 'userName' => $params['user'], + 'password' => $params['password'] ]); } diff --git a/core/src/plugins/access.webdav/class.webdavAccessDriver.php b/core/src/plugins/access.webdav/src/Driver.php similarity index 69% rename from core/src/plugins/access.webdav/class.webdavAccessDriver.php rename to core/src/plugins/access.webdav/src/Driver.php index ed6e3fb935..09a56b6665 100644 --- a/core/src/plugins/access.webdav/class.webdavAccessDriver.php +++ b/core/src/plugins/access.webdav/src/Driver.php @@ -20,19 +20,16 @@ * */ -defined('AJXP_EXEC') or die( 'Access not allowed'); - -use CoreAccess\Stream\Client\DAVClient; -use CoreAccess\Stream\StreamWrapper; +namespace Pydio\Access\WebDAV; -require_once __DIR__ . '/vendor/autoload.php'; +defined('AJXP_EXEC') or die( 'Access not allowed'); /** * AJXP_Plugin to access a webdav enabled server * @package AjaXplorer_Plugins * @subpackage Access */ -class webdavAccessDriver extends fsAccessDriver +class Driver extends \fsAccessDriver { /** * @var Repository @@ -58,6 +55,9 @@ public function detectStreamWrapper($register = false){ */ public function initRepository() { + + $this->detectStreamWrapper(true); + if (is_array($this->pluginConf)) { $this->driverConf = $this->pluginConf; } else { @@ -72,35 +72,23 @@ public function initRepository() // Connexion $settings = array( - 'baseUri' => $this->getSanitizedUrl($hostParts) . "/" . trim($path, '/'), + 'baseUri' => \AJXP_Utils::getSanitizedUrl($hostParts) . "/" . trim($path, '/'), ); - $this->client = new DAVClient($settings); + $this->client = new Client($settings); $this->client->registerStreamWrapper(); + // Params $recycle = $this->repository->getOption("RECYCLE_BIN"); // Config - ConfService::setConf("PROBE_REAL_SIZE", false); + \ConfService::setConf("PROBE_REAL_SIZE", false); $this->urlBase = "pydio://".$this->repository->getId(); if ($recycle != "") { - RecycleBinManager::init($this->urlBase, "/".$recycle); + \RecycleBinManager::init($this->urlBase, "/".$recycle); } } - /** - * Sanitize a URL removin all unwanted / trailing slashes - * - * @param array url_parts - * return string url - */ - public function getSanitizedUrl($arr) { - $credentials = join(':', array_filter([$arr['user'], $arr['pass']])); - $hostname = join(':', array_filter([$arr['host'], $arr['port']])); - $domain = join('@', array_filter([$credentials, $hostname])); - return $arr['scheme'] . '://' . join('/', array_filter([$domain, $arr['path']])); - } - } diff --git a/core/src/plugins/access.webdav/src/Wrapper.php b/core/src/plugins/access.webdav/src/Wrapper.php new file mode 100755 index 0000000000..e66f9989cc --- /dev/null +++ b/core/src/plugins/access.webdav/src/Wrapper.php @@ -0,0 +1,79 @@ + + * This file is part of Pydio. + * + * Pydio is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pydio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pydio. If not, see . + * + * The latest code can be found at . + * + */ +namespace Pydio\Access\WebDAV; + +defined('AJXP_EXEC') or die( 'Access not allowed'); + +require_once(AJXP_INSTALL_PATH."/plugins/access.fs/class.fsAccessWrapper.php"); + +/** + * @package AjaXplorer_Plugins + * @subpackage Access + */ +class Wrapper extends \fsAccessWrapper +{ + + const PROTOCOL = "pydio.dav"; + + /** + * Initialize the stream from the given path. + * Concretely, transform ajxp.dav:// into dav:// + * + * @param string $path + * @return mixed Real path or -1 if currentListing contains the listing : original path converted to real path + */ + protected static function initPath($path, $streamType, $storeOpenContext = false, $skipZip = false) + { + $url = parse_url($path); + $repoId = $url["host"]; + $repository = \ConfService::getRepositoryById($repoId); + + if (!isSet($repository)) { + $e = new Exception("Cannot find repository with id ".$repoId); + self::$lastException = $e; + throw $e; + } + + $credentials = \AJXP_Safe::tryLoadingCredentialsFromSources($url, $repository); + + $default = stream_context_get_options(stream_context_get_default()); + + $default[self::PROTOCOL]['user'] = $credentials['user']; + $default[self::PROTOCOL]['password'] = $credentials['password']; + + if (!isset($credentials['user'])) { + throw new \AJXP_Exception("Cannot find user/pass for Remote server access!"); + } + + $client = $default[self::PROTOCOL]['client']; + if (!isset($client)) { + throw new Exception("Client not configured"); + } + + $client->setConfig('defaults/request_options/auth', [$username, $password]); + stream_context_set_default($default); + + $url['scheme'] = self::PROTOCOL; + return \AJXP_Utils::getSanitizedUrl($url); + } + +} diff --git a/core/src/plugins/access.webdav/vendor/composer/autoload_psr4.php b/core/src/plugins/access.webdav/vendor/composer/autoload_psr4.php index 76f895331c..55b79705cf 100644 --- a/core/src/plugins/access.webdav/vendor/composer/autoload_psr4.php +++ b/core/src/plugins/access.webdav/vendor/composer/autoload_psr4.php @@ -6,6 +6,6 @@ $baseDir = dirname($vendorDir); return array( - 'CoreAccess\\Stream\\Client\\' => array($baseDir . '/src'), - 'CoreAccess\\' => array($baseDir . '/../core.access/src'), + 'Pydio\\Access\\WebDAV\\' => array($baseDir . '/src'), + 'Pydio\\Access\\Core\\' => array($baseDir . '/../core.access/src'), ); diff --git a/core/src/plugins/core.access/composer.json b/core/src/plugins/core.access/composer.json index f1b5e39e84..17c65b2a90 100644 --- a/core/src/plugins/core.access/composer.json +++ b/core/src/plugins/core.access/composer.json @@ -1,7 +1,10 @@ { - "autoload" : { + "name": "CoreAccess", + "description": "Pydio Core Access to Filesystems", + "homepage": "https://pydio.com/", + "autoload": { "psr-4": { - "CoreAccess\\": "src/" + "Pydio\\Access\\Core\\": "./src" } } } diff --git a/core/src/plugins/core.access/src/Stream/Client/AbstractClient.php b/core/src/plugins/core.access/src/Stream/Client.php similarity index 85% rename from core/src/plugins/core.access/src/Stream/Client/AbstractClient.php rename to core/src/plugins/core.access/src/Stream/Client.php index 573100e636..19900f3cfe 100755 --- a/core/src/plugins/core.access/src/Stream/Client/AbstractClient.php +++ b/core/src/plugins/core.access/src/Stream/Client.php @@ -19,7 +19,7 @@ * The latest code can be found at . */ -namespace CoreAccess\Stream\Client; +namespace Pydio\Access\Core\Stream; use GuzzleHttp\Command\Guzzle\GuzzleClient; @@ -30,7 +30,7 @@ * @link http://docs.aws.amazon.com/aws-sdk-php/v2/guide/service-s3.html User guide * @link http://docs.aws.amazon.com/aws-sdk-php/v2/api/class-Aws.S3.S3Client.html API docs */ -abstract class AbstractClient extends GuzzleClient +class Client extends GuzzleClient implements ClientInterface { const LATEST_API_VERSION = '2016-01-01'; @@ -68,7 +68,9 @@ public function __construct__( /** * Register this client on the StreamWrapper */ - public abstract function registerStreamWrapper(); + public function registerStreamWrapper() { + StreamWrapper::register($this); + } /** * Redefine a file stat @@ -77,7 +79,9 @@ public abstract function registerStreamWrapper(); * * @return array Associative array containing the stats */ - public abstract function formatUrlStat($arr); + public function formatUrlStat($arr) { + return $STAT_TEMPLATE; + } /** * Get a Directory Iterator based on the given array @@ -86,7 +90,9 @@ public abstract function formatUrlStat($arr); * * @return DirIterator */ - public abstract function getIterator($arr); + public function getIterator($arr) { + return new Iterator\DirIterator($arr); + } /** * Get the params from the passed path @@ -98,14 +104,6 @@ public abstract function getIterator($arr); public function getParams($path, $prefix = "") { $parts = parse_url($path); - $repositoryId = $parts["host"]; - $repository = \ConfService::getRepositoryById($repositoryId); - $credentials = \AJXP_Safe::tryLoadingCredentialsFromSources($parts, $repository); - - $username = $credentials["user"]; - $password = $credentials["password"]; - - $this->setConfig('defaults/request_options/auth', [$username, $password]); $parts[$prefix . 'path'] = $parts['path']; diff --git a/core/src/plugins/core.access/src/Stream/ClientInterface.php b/core/src/plugins/core.access/src/Stream/ClientInterface.php new file mode 100755 index 0000000000..ec5d3a19e5 --- /dev/null +++ b/core/src/plugins/core.access/src/Stream/ClientInterface.php @@ -0,0 +1,67 @@ + + * This file is part of Pydio. + * + * Pydio is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pydio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pydio. If not, see . + * + * The latest code can be found at . + */ + +namespace Pydio\Access\Core\Stream; + +use GuzzleHttp\Command\Guzzle\GuzzleClient\ClientInterface as GuzzleClientInterface; +use GuzzleHttp\Command\Guzzle\GuzzleClient\DescriptionInterface as DescriptionInterface; + +/** + * Client to interact with SabreDAV Client + * + * + * @link http://docs.aws.amazon.com/aws-sdk-php/v2/guide/service-s3.html User guide + * @link http://docs.aws.amazon.com/aws-sdk-php/v2/api/class-Aws.S3.S3Client.html API docs + */ +interface ClientInterface +{ + /** + * Register this client on the StreamWrapper + */ + public function registerStreamWrapper(); + + /** + * Redefine a file stat + * + * @param array $arr + * + * @return array Associative array containing the stats + */ + public function formatUrlStat($arr); + + /** + * Get a Directory Iterator based on the given array + * + * @param array $arr + * + * @return DirIterator + */ + public function getIterator($arr); + + /** + * Get the params from the passed path + * + * @param string $path Path passed to the stream wrapper + * + * @return array Hash of custom params + */ + public function getParams($path, $prefix = ""); +} diff --git a/core/src/plugins/core.access/src/Stream/Iterator/DirIterator.php b/core/src/plugins/core.access/src/Stream/Iterator/DirIterator.php index 26ebf24625..5f283c9a51 100644 --- a/core/src/plugins/core.access/src/Stream/Iterator/DirIterator.php +++ b/core/src/plugins/core.access/src/Stream/Iterator/DirIterator.php @@ -19,7 +19,7 @@ * The latest code can be found at . */ -namespace CoreAccess\Stream\Iterator; +namespace Pydio\Access\Core\Stream\Iterator; class DirIterator implements \Iterator { diff --git a/core/src/plugins/core.access/src/Stream/Listener/PathListener.php b/core/src/plugins/core.access/src/Stream/Listener/PathListener.php index 35bda9645b..ba16883740 100644 --- a/core/src/plugins/core.access/src/Stream/Listener/PathListener.php +++ b/core/src/plugins/core.access/src/Stream/Listener/PathListener.php @@ -19,7 +19,7 @@ * The latest code can be found at . */ -namespace CoreAccess\Stream\Listener; +namespace Pydio\Access\Core\Stream\Listener; use GuzzleHttp\Event\SubscriberInterface; use GuzzleHttp\Command\Event\PreparedEvent; diff --git a/core/src/plugins/core.access/src/Stream/StreamWrapper.php b/core/src/plugins/core.access/src/Stream/StreamWrapper.php index 0634ed5a4c..e2a2f1e859 100755 --- a/core/src/plugins/core.access/src/Stream/StreamWrapper.php +++ b/core/src/plugins/core.access/src/Stream/StreamWrapper.php @@ -19,10 +19,10 @@ * The latest code can be found at . */ -namespace CoreAccess\Stream; +namespace Pydio\Access\Core\Stream; use \AJXP_Utils; -use CoreAccess\Stream\Client\AbstractClient; +use Pydio\Access\Core\Stream\ClientInterface; use GuzzleHttp\Stream\Stream; use GuzzleHttp\Stream\CachingStream; @@ -65,6 +65,11 @@ class StreamWrapper extends \AJXP_SchemeTranslatorWrapper */ protected $objectIterator; + /** + * @var string the current protocol + */ + private $protocol = 's3'; + /** * @var array The next key to retrieve when using a directory iterator. Helps for fast directory traversal. */ @@ -75,9 +80,21 @@ class StreamWrapper extends \AJXP_SchemeTranslatorWrapper * * @param Client $client to use with the stream wrapper */ - public static function register(AbstractClient $client) - { - static::$client = $client; + public static function register( + ClientInterface $client, + $protocol + ) { + if (in_array($protocol, stream_get_wrappers())) { + stream_wrapper_unregister($protocol); + } + + // Set the client passed in as the default stream context client + stream_wrapper_register($protocol, get_called_class(), STREAM_IS_URL); + + $default = stream_context_get_options(stream_context_get_default()); + $default[$protocol]['client'] = $client; + + stream_context_set_default($default); } /** @@ -142,7 +159,7 @@ public function stream_flush() $params['body'] = $this->body; try { - static::$client->put($params); + $this->getClient()->put($params); return true; } catch (\Exception $e) { return $this->triggerError($e->getMessage()); @@ -208,7 +225,7 @@ public function unlink($path) { try { $this->clearStatInfo($path); - static::$client->delete($this->getParams($path)); + $this->getClient()->delete($this->getParams($path)); return true; } catch (\Exception $e) { return $this->triggerError($e->getMessage()); @@ -249,10 +266,10 @@ public function url_stat($path, $flags) return static::$nextStat[$key]; } - $result = static::$client->stat($params); + $result = $this->getClient()->stat($params); if ($result) { - $result = static::$client->formatUrlStat($result); + $result = $this->getClient()->formatUrlStat($result); static::$nextStat[$path] = $result; } @@ -275,7 +292,7 @@ public function mkdir($path, $mode, $options) { $params = $this->getParams($path); - $result = static::$client->mkdir($params); + $result = $this->getClient()->mkdir($params); return $result; } @@ -293,7 +310,7 @@ public function rmdir($path, $options) { $params = $this->getParams($path); - $result = static::$client->rmdir($params); + $result = $this->getClient()->rmdir($params); return $result; } @@ -311,9 +328,9 @@ public function dir_opendir($path, $options) { $params = $this->getParams($path); - $result = static::$client->ls($params); + $result = $this->getClient()->ls($params); - $this->objectIterator = static::$client->getIterator($result); + $this->objectIterator = $this->getClient()->getIterator($result); return true; } @@ -384,7 +401,7 @@ public function rename($path_from, $path_to) $this->clearStatInfo($path_from); $this->clearStatInfo($path_to); - $result = static::$client->rename($params); + $result = $this->getClient()->rename($params); return true; } @@ -398,7 +415,12 @@ public function rename($path_from, $path_to) */ protected function getParams($path, $prefix = "") { - return static::$client->getParams($path, $prefix); + $parts = explode('://', $path, 2); + $this->protocol = $parts[0]; + + $default = stream_context_get_options(stream_context_get_default()); + + return $this->getClient()->getParams($path, $prefix) + $default[$this->protocol]; } /** @@ -412,7 +434,7 @@ protected function getParams($path, $prefix = "") protected function openReadStream(array $params, array &$errors) { // Create the command and serialize the request - $response = static::$client->open($params); + $response = $this->getClient()->open($params); $this->body = $response->getBody(); @@ -446,7 +468,7 @@ protected function openAppendStream(array $params, array &$errors) { try { $this->body->seek(0, SEEK_END); - } catch (S3Exception $e) { + } catch (\Exception $e) { // The object does not exist, so use a simple write stream $this->openWriteStream($params, $errors); } @@ -554,4 +576,21 @@ public static function isSeekable($url) { public static function isRemote() { return true; } + + /** + * Gets the client from the stream context + * + * @return Client + * @throws Exception if no client has been configured + */ + private function getClient() + { + $default = stream_context_get_options(stream_context_get_default()); + + if (!$client = $default[$this->protocol]['client']) { + throw new \Exception('No client defined for '. $this->protocol); + } + + return $client; + } } diff --git a/core/src/plugins/core.access/vendor/composer/autoload_psr4.php b/core/src/plugins/core.access/vendor/composer/autoload_psr4.php index 8757646c13..f9068ac50e 100644 --- a/core/src/plugins/core.access/vendor/composer/autoload_psr4.php +++ b/core/src/plugins/core.access/vendor/composer/autoload_psr4.php @@ -6,5 +6,5 @@ $baseDir = dirname($vendorDir); return array( - 'CoreAccess\\' => array($baseDir . '/src'), + 'Pydio\\Access\\Core\\' => array($baseDir . '/src'), );