From d2255a1d304aa5e537e8aa097add2a51aa0e5d89 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 26 Aug 2014 18:46:07 +0200 Subject: [PATCH] New SMB storage backend --- apps/files_external/3rdparty/autoload.php | 7 + apps/files_external/3rdparty/composer.json | 13 + apps/files_external/3rdparty/composer.lock | 101 ++++ .../3rdparty/composer/ClassLoader.php | 413 ++++++++++++++ .../3rdparty/composer/autoload_classmap.php | 9 + .../3rdparty/composer/autoload_namespaces.php | 9 + .../3rdparty/composer/autoload_psr4.php | 13 + .../3rdparty/composer/autoload_real.php | 50 ++ .../3rdparty/composer/installed.json | 87 +++ .../3rdparty/icewind/smb/.gitignore | 2 + .../3rdparty/icewind/smb/.travis.yml | 50 ++ .../3rdparty/icewind/smb/LICENSE.txt | 19 + .../3rdparty/icewind/smb/README.md | 136 +++++ .../3rdparty/icewind/smb/composer.json | 24 + .../3rdparty/icewind/smb/src/Connection.php | 78 +++ .../3rdparty/icewind/smb/src/ErrorCodes.php | 28 + .../src/Exception/AccessDeniedException.php | 10 + .../src/Exception/AlreadyExistsException.php | 10 + .../src/Exception/AuthenticationException.php | 10 + .../smb/src/Exception/ConnectException.php | 10 + .../smb/src/Exception/ConnectionException.php | 10 + .../Exception/ConnectionRefusedException.php | 11 + .../icewind/smb/src/Exception/Exception.php | 10 + .../smb/src/Exception/FileInUseException.php | 10 + .../smb/src/Exception/ForbiddenException.php | 10 + .../smb/src/Exception/HostDownException.php | 11 + .../src/Exception/InvalidHostException.php | 10 + .../src/Exception/InvalidRequestException.php | 31 + .../src/Exception/InvalidTypeException.php | 10 + .../src/Exception/NoRouteToHostException.php | 11 + .../smb/src/Exception/NotEmptyException.php | 10 + .../smb/src/Exception/NotFoundException.php | 10 + .../smb/src/Exception/TimedOutException.php | 11 + .../3rdparty/icewind/smb/src/FileInfo.php | 126 +++++ .../3rdparty/icewind/smb/src/IFileInfo.php | 55 ++ .../3rdparty/icewind/smb/src/IShare.php | 134 +++++ .../icewind/smb/src/NativeFileInfo.php | 142 +++++ .../3rdparty/icewind/smb/src/NativeServer.php | 60 ++ .../3rdparty/icewind/smb/src/NativeShare.php | 288 ++++++++++ .../3rdparty/icewind/smb/src/NativeState.php | 308 ++++++++++ .../3rdparty/icewind/smb/src/NativeStream.php | 114 ++++ .../3rdparty/icewind/smb/src/Parser.php | 130 +++++ .../icewind/smb/src/RawConnection.php | 165 ++++++ .../3rdparty/icewind/smb/src/Server.php | 141 +++++ .../3rdparty/icewind/smb/src/Share.php | 394 +++++++++++++ .../icewind/smb/tests/AbstractShare.php | 534 ++++++++++++++++++ .../icewind/smb/tests/NativeShare.php | 27 + .../icewind/smb/tests/NativeStream.php | 143 +++++ .../3rdparty/icewind/smb/tests/Parser.php | 87 +++ .../3rdparty/icewind/smb/tests/Server.php | 57 ++ .../3rdparty/icewind/smb/tests/Share.php | 24 + .../3rdparty/icewind/smb/tests/bootstrap.php | 9 + .../3rdparty/icewind/smb/tests/config.json | 7 + .../3rdparty/icewind/smb/tests/phpunit.xml | 6 + .../3rdparty/icewind/streams/.gitignore | 3 + .../3rdparty/icewind/streams/.travis.yml | 26 + .../3rdparty/icewind/streams/README.md | 52 ++ .../3rdparty/icewind/streams/composer.json | 23 + .../icewind/streams/src/CallbackWrapper.php | 110 ++++ .../icewind/streams/src/Directory.php | 35 ++ .../3rdparty/icewind/streams/src/File.php | 86 +++ .../icewind/streams/src/IteratorDirectory.php | 123 ++++ .../icewind/streams/src/NullWrapper.php | 42 ++ .../3rdparty/icewind/streams/src/Wrapper.php | 110 ++++ .../icewind/streams/tests/CallbackWrapper.php | 72 +++ .../streams/tests/IteratorDirectory.php | 130 +++++ .../icewind/streams/tests/NullWrapper.php | 59 ++ .../icewind/streams/tests/Wrapper.php | 105 ++++ .../icewind/streams/tests/bootstrap.php | 9 + .../icewind/streams/tests/phpunit.xml | 6 + apps/files_external/3rdparty/smb4php/smb.php | 516 ----------------- apps/files_external/appinfo/app.php | 2 + apps/files_external/lib/smb.php | 294 +++++++--- apps/files_external/lib/smb_oc.php | 45 +- apps/files_external/tests/backends/smb.php | 2 +- 75 files changed, 5407 insertions(+), 628 deletions(-) create mode 100644 apps/files_external/3rdparty/autoload.php create mode 100644 apps/files_external/3rdparty/composer.json create mode 100644 apps/files_external/3rdparty/composer.lock create mode 100644 apps/files_external/3rdparty/composer/ClassLoader.php create mode 100644 apps/files_external/3rdparty/composer/autoload_classmap.php create mode 100644 apps/files_external/3rdparty/composer/autoload_namespaces.php create mode 100644 apps/files_external/3rdparty/composer/autoload_psr4.php create mode 100644 apps/files_external/3rdparty/composer/autoload_real.php create mode 100644 apps/files_external/3rdparty/composer/installed.json create mode 100644 apps/files_external/3rdparty/icewind/smb/.gitignore create mode 100644 apps/files_external/3rdparty/icewind/smb/.travis.yml create mode 100644 apps/files_external/3rdparty/icewind/smb/LICENSE.txt create mode 100644 apps/files_external/3rdparty/icewind/smb/README.md create mode 100644 apps/files_external/3rdparty/icewind/smb/composer.json create mode 100644 apps/files_external/3rdparty/icewind/smb/src/Connection.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/ErrorCodes.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/Exception/AccessDeniedException.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/Exception/AlreadyExistsException.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/Exception/AuthenticationException.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/Exception/ConnectException.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/Exception/ConnectionException.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/Exception/ConnectionRefusedException.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/Exception/Exception.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/Exception/FileInUseException.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/Exception/ForbiddenException.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/Exception/HostDownException.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/Exception/InvalidHostException.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/Exception/InvalidRequestException.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/Exception/InvalidTypeException.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/Exception/NoRouteToHostException.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/Exception/NotEmptyException.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/Exception/NotFoundException.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/Exception/TimedOutException.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/FileInfo.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/IFileInfo.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/IShare.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/NativeFileInfo.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/NativeServer.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/NativeShare.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/NativeState.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/NativeStream.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/Parser.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/RawConnection.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/Server.php create mode 100644 apps/files_external/3rdparty/icewind/smb/src/Share.php create mode 100644 apps/files_external/3rdparty/icewind/smb/tests/AbstractShare.php create mode 100644 apps/files_external/3rdparty/icewind/smb/tests/NativeShare.php create mode 100644 apps/files_external/3rdparty/icewind/smb/tests/NativeStream.php create mode 100644 apps/files_external/3rdparty/icewind/smb/tests/Parser.php create mode 100644 apps/files_external/3rdparty/icewind/smb/tests/Server.php create mode 100644 apps/files_external/3rdparty/icewind/smb/tests/Share.php create mode 100644 apps/files_external/3rdparty/icewind/smb/tests/bootstrap.php create mode 100644 apps/files_external/3rdparty/icewind/smb/tests/config.json create mode 100644 apps/files_external/3rdparty/icewind/smb/tests/phpunit.xml create mode 100644 apps/files_external/3rdparty/icewind/streams/.gitignore create mode 100644 apps/files_external/3rdparty/icewind/streams/.travis.yml create mode 100644 apps/files_external/3rdparty/icewind/streams/README.md create mode 100644 apps/files_external/3rdparty/icewind/streams/composer.json create mode 100644 apps/files_external/3rdparty/icewind/streams/src/CallbackWrapper.php create mode 100644 apps/files_external/3rdparty/icewind/streams/src/Directory.php create mode 100644 apps/files_external/3rdparty/icewind/streams/src/File.php create mode 100644 apps/files_external/3rdparty/icewind/streams/src/IteratorDirectory.php create mode 100644 apps/files_external/3rdparty/icewind/streams/src/NullWrapper.php create mode 100644 apps/files_external/3rdparty/icewind/streams/src/Wrapper.php create mode 100644 apps/files_external/3rdparty/icewind/streams/tests/CallbackWrapper.php create mode 100644 apps/files_external/3rdparty/icewind/streams/tests/IteratorDirectory.php create mode 100644 apps/files_external/3rdparty/icewind/streams/tests/NullWrapper.php create mode 100644 apps/files_external/3rdparty/icewind/streams/tests/Wrapper.php create mode 100644 apps/files_external/3rdparty/icewind/streams/tests/bootstrap.php create mode 100644 apps/files_external/3rdparty/icewind/streams/tests/phpunit.xml delete mode 100644 apps/files_external/3rdparty/smb4php/smb.php diff --git a/apps/files_external/3rdparty/autoload.php b/apps/files_external/3rdparty/autoload.php new file mode 100644 index 00000000000..78e3de4ca0c --- /dev/null +++ b/apps/files_external/3rdparty/autoload.php @@ -0,0 +1,7 @@ +=5.3" + }, + "require-dev": { + "satooshi/php-coveralls": "dev-master" + }, + "type": "library", + "autoload": { + "psr-4": { + "Icewind\\SMB\\": "src/", + "Icewind\\SMB\\Test\\": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Robin Appelman", + "email": "icewind@owncloud.com" + } + ], + "description": "php wrapper for smbclient and libsmbclient-php", + "time": "2015-02-10 16:37:37" + }, + { + "name": "icewind/streams", + "version": "0.2", + "source": { + "type": "git", + "url": "https://github.com/icewind1991/Streams.git", + "reference": "5aae45f2ddd3d1a6e2a496dd5d1e7857bfeb605a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/icewind1991/Streams/zipball/5aae45f2ddd3d1a6e2a496dd5d1e7857bfeb605a", + "reference": "5aae45f2ddd3d1a6e2a496dd5d1e7857bfeb605a", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "require-dev": { + "satooshi/php-coveralls": "dev-master" + }, + "type": "library", + "autoload": { + "psr-4": { + "Icewind\\Streams\\Tests\\": "tests/", + "Icewind\\Streams\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Robin Appelman", + "email": "icewind@owncloud.com" + } + ], + "description": "A set of generic stream wrappers", + "time": "2014-07-30 23:46:15" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": { + "icewind/smb": 20 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/apps/files_external/3rdparty/composer/ClassLoader.php b/apps/files_external/3rdparty/composer/ClassLoader.php new file mode 100644 index 00000000000..5e1469e8307 --- /dev/null +++ b/apps/files_external/3rdparty/composer/ClassLoader.php @@ -0,0 +1,413 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0 class loader + * + * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + */ +class ClassLoader +{ + // PSR-4 + private $prefixLengthsPsr4 = array(); + private $prefixDirsPsr4 = array(); + private $fallbackDirsPsr4 = array(); + + // PSR-0 + private $prefixesPsr0 = array(); + private $fallbackDirsPsr0 = array(); + + private $useIncludePath = false; + private $classMap = array(); + + private $classMapAuthoritative = false; + + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', $this->prefixesPsr0); + } + + return array(); + } + + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-0 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 base directories + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return bool|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + includeFile($file); + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 + if ('\\' == $class[0]) { + $class = substr($class, 1); + } + + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative) { + return false; + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if ($file === null && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if ($file === null) { + // Remember that this class does not exist. + return $this->classMap[$class] = false; + } + + return $file; + } + + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { + if (0 === strpos($class, $prefix)) { + foreach ($this->prefixDirsPsr4[$prefix] as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + */ +function includeFile($file) +{ + include $file; +} diff --git a/apps/files_external/3rdparty/composer/autoload_classmap.php b/apps/files_external/3rdparty/composer/autoload_classmap.php new file mode 100644 index 00000000000..1bd6482f9ea --- /dev/null +++ b/apps/files_external/3rdparty/composer/autoload_classmap.php @@ -0,0 +1,9 @@ + array($vendorDir . '/icewind/streams/tests'), + 'Icewind\\Streams\\' => array($vendorDir . '/icewind/streams/src'), + 'Icewind\\SMB\\Test\\' => array($vendorDir . '/icewind/smb/tests'), + 'Icewind\\SMB\\' => array($vendorDir . '/icewind/smb/src'), +); diff --git a/apps/files_external/3rdparty/composer/autoload_real.php b/apps/files_external/3rdparty/composer/autoload_real.php new file mode 100644 index 00000000000..3391b322be0 --- /dev/null +++ b/apps/files_external/3rdparty/composer/autoload_real.php @@ -0,0 +1,50 @@ + $path) { + $loader->set($namespace, $path); + } + + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + + $loader->register(true); + + return $loader; + } +} + +function composerRequire98fe9b281934250b3a93f69a5ce843b3($file) +{ + require $file; +} diff --git a/apps/files_external/3rdparty/composer/installed.json b/apps/files_external/3rdparty/composer/installed.json new file mode 100644 index 00000000000..03bad8abf36 --- /dev/null +++ b/apps/files_external/3rdparty/composer/installed.json @@ -0,0 +1,87 @@ +[ + { + "name": "icewind/streams", + "version": "0.2", + "version_normalized": "0.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/icewind1991/Streams.git", + "reference": "5aae45f2ddd3d1a6e2a496dd5d1e7857bfeb605a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/icewind1991/Streams/zipball/5aae45f2ddd3d1a6e2a496dd5d1e7857bfeb605a", + "reference": "5aae45f2ddd3d1a6e2a496dd5d1e7857bfeb605a", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "require-dev": { + "satooshi/php-coveralls": "dev-master" + }, + "time": "2014-07-30 23:46:15", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Icewind\\Streams\\Tests\\": "tests/", + "Icewind\\Streams\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Robin Appelman", + "email": "icewind@owncloud.com" + } + ], + "description": "A set of generic stream wrappers" + }, + { + "name": "icewind/smb", + "version": "dev-master", + "version_normalized": "9999999-dev", + "source": { + "type": "git", + "url": "https://github.com/icewind1991/SMB.git", + "reference": "ededbfbaa3d7124ce8d4b6c119cd225fa342916d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/icewind1991/SMB/zipball/ededbfbaa3d7124ce8d4b6c119cd225fa342916d", + "reference": "ededbfbaa3d7124ce8d4b6c119cd225fa342916d", + "shasum": "" + }, + "require": { + "icewind/streams": "0.2.x", + "php": ">=5.3" + }, + "require-dev": { + "satooshi/php-coveralls": "dev-master" + }, + "time": "2015-02-10 16:37:37", + "type": "library", + "installation-source": "source", + "autoload": { + "psr-4": { + "Icewind\\SMB\\": "src/", + "Icewind\\SMB\\Test\\": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Robin Appelman", + "email": "icewind@owncloud.com" + } + ], + "description": "php wrapper for smbclient and libsmbclient-php" + } +] diff --git a/apps/files_external/3rdparty/icewind/smb/.gitignore b/apps/files_external/3rdparty/icewind/smb/.gitignore new file mode 100644 index 00000000000..3ce5adbbde5 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/.gitignore @@ -0,0 +1,2 @@ +.idea +vendor diff --git a/apps/files_external/3rdparty/icewind/smb/.travis.yml b/apps/files_external/3rdparty/icewind/smb/.travis.yml new file mode 100644 index 00000000000..c1ac3727d08 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/.travis.yml @@ -0,0 +1,50 @@ +language: php +php: + - 5.3 + - 5.4 + - 5.5 + +env: + global: + - CURRENT_DIR=`pwd` + +before_install: + - pass=$(perl -e 'print crypt("test", "password")') + - sudo useradd -m -p $pass test + - sudo apt-get update -qq + - sudo apt-get install samba smbclient libsmbclient-dev libsmbclient + - wget -O /tmp/libsmbclient-php.zip https://github.com/eduardok/libsmbclient-php/archive/master.zip + - unzip /tmp/libsmbclient-php.zip -d /tmp + - cd /tmp/libsmbclient-php-master + - phpize && ./configure && make && sudo make install + - echo 'extension="libsmbclient.so"' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini + - cd $CURRENT_DIR + - chmod go+w $HOME + - printf "%s\n%s\n" test test|sudo smbpasswd -s test + - sudo mkdir /home/test/test + - sudo chown test /home/test/test + - | + echo "[test] + comment = test + path = /home/test + guest ok = yes + writeable = yes + map archive = yes + map system = yes + map hidden = yes + create mask = 0777 + inherit permissions = yes" | sudo tee -a /etc/samba/smb.conf + - sudo service smbd restart + - testparm -s + +install: + - composer install --dev --no-interaction + +script: + - mkdir -p build/logs + - cd tests + - phpunit --coverage-clover ../build/logs/clover.xml --configuration phpunit.xml + +after_script: + - cd $CURRENT_DIR + - php vendor/bin/coveralls -v diff --git a/apps/files_external/3rdparty/icewind/smb/LICENSE.txt b/apps/files_external/3rdparty/icewind/smb/LICENSE.txt new file mode 100644 index 00000000000..16e3a1004ec --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2014 Robin Appelman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/apps/files_external/3rdparty/icewind/smb/README.md b/apps/files_external/3rdparty/icewind/smb/README.md new file mode 100644 index 00000000000..a0864717b09 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/README.md @@ -0,0 +1,136 @@ +SMB +=== + +[![Coverage Status](https://img.shields.io/coveralls/icewind1991/SMB.svg)](https://coveralls.io/r/icewind1991/SMB?branch=master) +[![Build Status](https://travis-ci.org/icewind1991/SMB.svg?branch=master)](https://travis-ci.org/icewind1991/SMB) +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/icewind1991/SMB/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/icewind1991/SMB/?branch=master) + +PHP wrapper for `smbclient` and [`libsmbclient-php`](https://github.com/eduardok/libsmbclient-php) + +- Reuses a single `smbclient` instance for multiple requests +- Doesn't leak the password to the process list +- Simple 1-on-1 mapping of SMB commands +- A stream-based api to remove the need for temporary files +- Support for using libsmbclient directly trough [`libsmbclient-php`](https://github.com/eduardok/libsmbclient-php) + +Examples +---- + +### Upload a file ### + +```php +getShare('test'); +$share->put($fileToUpload, 'example.txt'); +``` + +### Download a file ### + +```php +getShare('test'); +$share->get('example.txt', $target); +``` + +### List shares on the remote server ### + +```php +listShares(); + +foreach ($shares as $share) { + echo $share->getName() . "\n"; +} +``` + +### List the content of a folder ### + +```php +getShare('test'); +$content = $share->dir('test'); + +foreach ($content as $info) { + echo $name->getName() . "\n"; + echo "\tsize :" . $info->getSize() . "\n"; +} +``` + +### Using read streams + +```php +getShare('test'); + +$fh = $share->read('test.txt'); +echo fread($fh, 4086); +fclose($fh); +``` + +### Using write streams + +```php +getShare('test'); + +$fh = $share->write('test.txt'); +fwrite($fh, 'bar'); +fclose($fh); +``` + +### Using libsmbclient-php ### + +Install [libsmbclient-php](https://github.com/eduardok/libsmbclient-php) + +```php +getShare('test'); +$share->put($fileToUpload, 'example.txt'); +``` diff --git a/apps/files_external/3rdparty/icewind/smb/composer.json b/apps/files_external/3rdparty/icewind/smb/composer.json new file mode 100644 index 00000000000..92f4280d775 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/composer.json @@ -0,0 +1,24 @@ +{ + "name" : "icewind/smb", + "description" : "php wrapper for smbclient and libsmbclient-php", + "license" : "MIT", + "authors" : [ + { + "name" : "Robin Appelman", + "email": "icewind@owncloud.com" + } + ], + "require" : { + "php": ">=5.3", + "icewind/streams": "0.2.x" + }, + "require-dev": { + "satooshi/php-coveralls" : "dev-master" + }, + "autoload" : { + "psr-4": { + "Icewind\\SMB\\": "src/", + "Icewind\\SMB\\Test\\": "tests/" + } + } +} diff --git a/apps/files_external/3rdparty/icewind/smb/src/Connection.php b/apps/files_external/3rdparty/icewind/smb/src/Connection.php new file mode 100644 index 00000000000..6191b11ac8e --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/Connection.php @@ -0,0 +1,78 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB; + +use Icewind\SMB\Exception\AuthenticationException; +use Icewind\SMB\Exception\ConnectionException; +use Icewind\SMB\Exception\InvalidHostException; + +class Connection extends RawConnection { + const DELIMITER = 'smb:'; + + /** + * send input to smbclient + * + * @param string $input + */ + public function write($input) { + parent::write($input . PHP_EOL); + } + + /** + * get all unprocessed output from smbclient until the next prompt + * + * @throws ConnectionException + * @return string + */ + public function read() { + if (!$this->isValid()) { + throw new ConnectionException(); + } + $line = $this->readLine(); //first line is prompt + $this->checkConnectionError($line); + + $output = array(); + $line = $this->readLine(); + $length = mb_strlen(self::DELIMITER); + while (mb_substr($line, 0, $length) !== self::DELIMITER) { //next prompt functions as delimiter + $output[] .= $line; + $line = $this->readLine(); + } + return $output; + } + + /** + * check if the first line holds a connection failure + * + * @param $line + * @throws AuthenticationException + * @throws InvalidHostException + */ + private function checkConnectionError($line) { + $line = rtrim($line, ')'); + if (substr($line, -23) === ErrorCodes::LogonFailure) { + throw new AuthenticationException(); + } + if (substr($line, -26) === ErrorCodes::BadHostName) { + throw new InvalidHostException(); + } + if (substr($line, -22) === ErrorCodes::Unsuccessful) { + throw new InvalidHostException(); + } + if (substr($line, -28) === ErrorCodes::ConnectionRefused) { + throw new InvalidHostException(); + } + } + + public function close($terminate = true) { + if (is_resource($this->getInputStream())) { + $this->write('close' . PHP_EOL); + } + parent::close($terminate); + } +} diff --git a/apps/files_external/3rdparty/icewind/smb/src/ErrorCodes.php b/apps/files_external/3rdparty/icewind/smb/src/ErrorCodes.php new file mode 100644 index 00000000000..d9f2507cba9 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/ErrorCodes.php @@ -0,0 +1,28 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB; + +class ErrorCodes { + /** + * connection errors + */ + const LogonFailure = 'NT_STATUS_LOGON_FAILURE'; + const BadHostName = 'NT_STATUS_BAD_NETWORK_NAME'; + const Unsuccessful = 'NT_STATUS_UNSUCCESSFUL'; + const ConnectionRefused = 'NT_STATUS_CONNECTION_REFUSED'; + + const PathNotFound = 'NT_STATUS_OBJECT_PATH_NOT_FOUND'; + const NoSuchFile = 'NT_STATUS_NO_SUCH_FILE'; + const ObjectNotFound = 'NT_STATUS_OBJECT_NAME_NOT_FOUND'; + const NameCollision = 'NT_STATUS_OBJECT_NAME_COLLISION'; + const AccessDenied = 'NT_STATUS_ACCESS_DENIED'; + const DirectoryNotEmpty = 'NT_STATUS_DIRECTORY_NOT_EMPTY'; + const FileIsADirectory = 'NT_STATUS_FILE_IS_A_DIRECTORY'; + const NotADirectory = 'NT_STATUS_NOT_A_DIRECTORY'; + const SharingViolation = 'NT_STATUS_SHARING_VIOLATION'; +} diff --git a/apps/files_external/3rdparty/icewind/smb/src/Exception/AccessDeniedException.php b/apps/files_external/3rdparty/icewind/smb/src/Exception/AccessDeniedException.php new file mode 100644 index 00000000000..4b7d22b3540 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/Exception/AccessDeniedException.php @@ -0,0 +1,10 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Exception; + +class AccessDeniedException extends ConnectException {} diff --git a/apps/files_external/3rdparty/icewind/smb/src/Exception/AlreadyExistsException.php b/apps/files_external/3rdparty/icewind/smb/src/Exception/AlreadyExistsException.php new file mode 100644 index 00000000000..4636b9d1786 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/Exception/AlreadyExistsException.php @@ -0,0 +1,10 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Exception; + +class AlreadyExistsException extends InvalidRequestException {} diff --git a/apps/files_external/3rdparty/icewind/smb/src/Exception/AuthenticationException.php b/apps/files_external/3rdparty/icewind/smb/src/Exception/AuthenticationException.php new file mode 100644 index 00000000000..ddf5d7e34dc --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/Exception/AuthenticationException.php @@ -0,0 +1,10 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Exception; + +class AuthenticationException extends ConnectException{} diff --git a/apps/files_external/3rdparty/icewind/smb/src/Exception/ConnectException.php b/apps/files_external/3rdparty/icewind/smb/src/Exception/ConnectException.php new file mode 100644 index 00000000000..310d441bf5c --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/Exception/ConnectException.php @@ -0,0 +1,10 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Exception; + +class ConnectException extends Exception {} diff --git a/apps/files_external/3rdparty/icewind/smb/src/Exception/ConnectionException.php b/apps/files_external/3rdparty/icewind/smb/src/Exception/ConnectionException.php new file mode 100644 index 00000000000..21033560fc0 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/Exception/ConnectionException.php @@ -0,0 +1,10 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Exception; + +class ConnectionException extends ConnectException {} diff --git a/apps/files_external/3rdparty/icewind/smb/src/Exception/ConnectionRefusedException.php b/apps/files_external/3rdparty/icewind/smb/src/Exception/ConnectionRefusedException.php new file mode 100644 index 00000000000..ffebb4c8a51 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/Exception/ConnectionRefusedException.php @@ -0,0 +1,11 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Exception; + +class ConnectionRefusedException extends ConnectException { +} diff --git a/apps/files_external/3rdparty/icewind/smb/src/Exception/Exception.php b/apps/files_external/3rdparty/icewind/smb/src/Exception/Exception.php new file mode 100644 index 00000000000..3307ad57a24 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/Exception/Exception.php @@ -0,0 +1,10 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Exception; + +class Exception extends \Exception {} diff --git a/apps/files_external/3rdparty/icewind/smb/src/Exception/FileInUseException.php b/apps/files_external/3rdparty/icewind/smb/src/Exception/FileInUseException.php new file mode 100644 index 00000000000..46460eb6936 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/Exception/FileInUseException.php @@ -0,0 +1,10 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Exception; + +class FileInUseException extends InvalidRequestException {} diff --git a/apps/files_external/3rdparty/icewind/smb/src/Exception/ForbiddenException.php b/apps/files_external/3rdparty/icewind/smb/src/Exception/ForbiddenException.php new file mode 100644 index 00000000000..117d6a438ea --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/Exception/ForbiddenException.php @@ -0,0 +1,10 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Exception; + +class ForbiddenException extends InvalidRequestException {} diff --git a/apps/files_external/3rdparty/icewind/smb/src/Exception/HostDownException.php b/apps/files_external/3rdparty/icewind/smb/src/Exception/HostDownException.php new file mode 100644 index 00000000000..9ae762b6108 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/Exception/HostDownException.php @@ -0,0 +1,11 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Exception; + +class HostDownException extends ConnectException { +} diff --git a/apps/files_external/3rdparty/icewind/smb/src/Exception/InvalidHostException.php b/apps/files_external/3rdparty/icewind/smb/src/Exception/InvalidHostException.php new file mode 100644 index 00000000000..4b17f1a4de6 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/Exception/InvalidHostException.php @@ -0,0 +1,10 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Exception; + +class InvalidHostException extends ConnectException {} diff --git a/apps/files_external/3rdparty/icewind/smb/src/Exception/InvalidRequestException.php b/apps/files_external/3rdparty/icewind/smb/src/Exception/InvalidRequestException.php new file mode 100644 index 00000000000..0dccd8b909e --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/Exception/InvalidRequestException.php @@ -0,0 +1,31 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Exception; + +class InvalidRequestException extends Exception { + /** + * @var string + */ + protected $path; + + /** + * @param string $path + * @param int $code + */ + public function __construct($path, $code = 0) { + parent::__construct('Invalid request for ' . $path, $code); + $this->path = $path; + } + + /** + * @return string + */ + public function getPath() { + return $this->path; + } +} diff --git a/apps/files_external/3rdparty/icewind/smb/src/Exception/InvalidTypeException.php b/apps/files_external/3rdparty/icewind/smb/src/Exception/InvalidTypeException.php new file mode 100644 index 00000000000..63b79305ad4 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/Exception/InvalidTypeException.php @@ -0,0 +1,10 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Exception; + +class InvalidTypeException extends InvalidRequestException {} diff --git a/apps/files_external/3rdparty/icewind/smb/src/Exception/NoRouteToHostException.php b/apps/files_external/3rdparty/icewind/smb/src/Exception/NoRouteToHostException.php new file mode 100644 index 00000000000..03daf36d610 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/Exception/NoRouteToHostException.php @@ -0,0 +1,11 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Exception; + +class NoRouteToHostException extends ConnectException { +} diff --git a/apps/files_external/3rdparty/icewind/smb/src/Exception/NotEmptyException.php b/apps/files_external/3rdparty/icewind/smb/src/Exception/NotEmptyException.php new file mode 100644 index 00000000000..789a76bcb9e --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/Exception/NotEmptyException.php @@ -0,0 +1,10 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Exception; + +class NotEmptyException extends InvalidRequestException {} diff --git a/apps/files_external/3rdparty/icewind/smb/src/Exception/NotFoundException.php b/apps/files_external/3rdparty/icewind/smb/src/Exception/NotFoundException.php new file mode 100644 index 00000000000..9ce97cd535b --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/Exception/NotFoundException.php @@ -0,0 +1,10 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Exception; + +class NotFoundException extends InvalidRequestException {} diff --git a/apps/files_external/3rdparty/icewind/smb/src/Exception/TimedOutException.php b/apps/files_external/3rdparty/icewind/smb/src/Exception/TimedOutException.php new file mode 100644 index 00000000000..dd57c9b8ccc --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/Exception/TimedOutException.php @@ -0,0 +1,11 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Exception; + +class TimedOutException extends ConnectException { +} diff --git a/apps/files_external/3rdparty/icewind/smb/src/FileInfo.php b/apps/files_external/3rdparty/icewind/smb/src/FileInfo.php new file mode 100644 index 00000000000..ef9121a8f57 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/FileInfo.php @@ -0,0 +1,126 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB; + +class FileInfo implements IFileInfo { + /* + * Mappings of the DOS mode bits, as returned by smbc_getxattr() when the + * attribute name "system.dos_attr.mode" (or "system.dos_attr.*" or + * "system.*") is specified. + */ + const MODE_READONLY = 0x01; + const MODE_HIDDEN = 0x02; + const MODE_SYSTEM = 0x04; + const MODE_VOLUME_ID = 0x08; + const MODE_DIRECTORY = 0x10; + const MODE_ARCHIVE = 0x20; + const MODE_NORMAL = 0x80; + + /** + * @var string + */ + protected $path; + + /** + * @var string + */ + protected $name; + + /** + * @var int + */ + protected $size; + + /** + * @var int + */ + protected $time; + + /** + * @var int + */ + protected $mode; + + /** + * @param string $path + * @param string $name + * @param int $size + * @param int $time + * @param int $mode + */ + public function __construct($path, $name, $size, $time, $mode) { + $this->path = $path; + $this->name = $name; + $this->size = $size; + $this->time = $time; + $this->mode = $mode; + } + + /** + * @return string + */ + public function getPath() { + return $this->path; + } + + /** + * @return string + */ + public function getName() { + return $this->name; + } + + /** + * @return int + */ + public function getSize() { + return $this->size; + } + + /** + * @return int + */ + public function getMTime() { + return $this->time; + } + + /** + * @return bool + */ + public function isDirectory() { + return (bool)($this->mode & self::MODE_DIRECTORY); + } + + /** + * @return bool + */ + public function isReadOnly() { + return (bool)($this->mode & self::MODE_READONLY); + } + + /** + * @return bool + */ + public function isHidden() { + return (bool)($this->mode & self::MODE_HIDDEN); + } + + /** + * @return bool + */ + public function isSystem() { + return (bool)($this->mode & self::MODE_SYSTEM); + } + + /** + * @return bool + */ + public function isArchived() { + return (bool)($this->mode & self::MODE_ARCHIVE); + } +} diff --git a/apps/files_external/3rdparty/icewind/smb/src/IFileInfo.php b/apps/files_external/3rdparty/icewind/smb/src/IFileInfo.php new file mode 100644 index 00000000000..16ea8cfbd04 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/IFileInfo.php @@ -0,0 +1,55 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB; + +interface IFileInfo { + /** + * @return string + */ + public function getPath(); + + /** + * @return string + */ + public function getName(); + + /** + * @return int + */ + public function getSize(); + + /** + * @return int + */ + public function getMTime(); + + /** + * @return bool + */ + public function isDirectory(); + + /** + * @return bool + */ + public function isReadOnly(); + + /** + * @return bool + */ + public function isHidden(); + + /** + * @return bool + */ + public function isSystem(); + + /** + * @return bool + */ + public function isArchived(); +} diff --git a/apps/files_external/3rdparty/icewind/smb/src/IShare.php b/apps/files_external/3rdparty/icewind/smb/src/IShare.php new file mode 100644 index 00000000000..4851e9de053 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/IShare.php @@ -0,0 +1,134 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB; + +interface IShare { + /** + * Get the name of the share + * + * @return string + */ + public function getName(); + + /** + * Download a remote file + * + * @param string $source remove file + * @param string $target local file + * @return bool + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\InvalidTypeException + */ + public function get($source, $target); + + /** + * Upload a local file + * + * @param string $source local file + * @param string $target remove file + * @return bool + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\InvalidTypeException + */ + public function put($source, $target); + + /** + * Open a readable stream top a remote file + * + * @param string $source + * @return resource a read only stream with the contents of the remote file + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\InvalidTypeException + */ + public function read($source); + + /** + * Open a writable stream to a remote file + * + * @param string $target + * @return resource a write only stream to upload a remote file + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\InvalidTypeException + */ + public function write($target); + + /** + * Rename a remote file + * + * @param string $from + * @param string $to + * @return bool + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\AlreadyExistsException + */ + public function rename($from, $to); + + /** + * Delete a file on the share + * + * @param string $path + * @return bool + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\InvalidTypeException + */ + public function del($path); + + /** + * List the content of a remote folder + * + * @param $path + * @return \Icewind\SMB\IFileInfo[] + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\InvalidTypeException + */ + public function dir($path); + + /** + * @param string $path + * @return \Icewind\SMB\IFileInfo + * + * @throws \Icewind\SMB\Exception\NotFoundException + */ + public function stat($path); + + /** + * Create a folder on the share + * + * @param string $path + * @return bool + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\AlreadyExistsException + */ + public function mkdir($path); + + /** + * Remove a folder on the share + * + * @param string $path + * @return bool + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\InvalidTypeException + */ + public function rmdir($path); + + /** + * @param string $path + * @param int $mode a combination of FileInfo::MODE_READONLY, FileInfo::MODE_ARCHIVE, FileInfo::MODE_SYSTEM and FileInfo::MODE_HIDDEN, FileInfo::NORMAL + * @return mixed + */ + public function setMode($path, $mode); +} diff --git a/apps/files_external/3rdparty/icewind/smb/src/NativeFileInfo.php b/apps/files_external/3rdparty/icewind/smb/src/NativeFileInfo.php new file mode 100644 index 00000000000..6ef5cf0c5b9 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/NativeFileInfo.php @@ -0,0 +1,142 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB; + +class NativeFileInfo implements IFileInfo { + const MODE_FILE = 0100000; + + /** + * @var string + */ + protected $path; + + /** + * @var string + */ + protected $name; + + /** + * @var \Icewind\SMB\NativeShare + */ + protected $share; + + /** + * @var array | null + */ + protected $statCache; + + /** + * @var int + */ + protected $modeCache; + + /** + * @param \Icewind\SMB\NativeShare $share + * @param string $path + * @param string $name + * @param array $stat + */ + public function __construct($share, $path, $name, $stat = null) { + $this->share = $share; + $this->path = $path; + $this->name = $name; + $this->statCache = $stat; + } + + /** + * @return string + */ + public function getPath() { + return $this->path; + } + + /** + * @return string + */ + public function getName() { + return $this->name; + } + + /** + * @return array + */ + protected function stat() { + if (!$this->statCache) { + $this->statCache = $this->share->getStat($this->getPath()); + } + return $this->statCache; + } + + /** + * @return int + */ + public function getSize() { + $stat = $this->stat(); + return $stat['size']; + } + + /** + * @return int + */ + public function getMTime() { + $stat = $this->stat(); + return $stat['mtime']; + } + + /** + * @return bool + */ + public function isDirectory() { + $stat = $this->stat(); + return !($stat['mode'] & self::MODE_FILE); + } + + /** + * @return int + */ + protected function getMode() { + if (!$this->modeCache) { + $attribute = $this->share->getAttribute($this->path, 'system.dos_attr.mode'); + // parse hex string + $this->modeCache = (int)hexdec(substr($attribute, 2)); + } + return $this->modeCache; + } + + /** + * @return bool + */ + public function isReadOnly() { + $mode = $this->getMode(); + return (bool)($mode & FileInfo::MODE_READONLY); + } + + /** + * @return bool + */ + public function isHidden() { + $mode = $this->getMode(); + return (bool)($mode & FileInfo::MODE_HIDDEN); + } + + /** + * @return bool + */ + public function isSystem() { + $mode = $this->getMode(); + return (bool)($mode & FileInfo::MODE_SYSTEM); + } + + /** + * @return bool + */ + public function isArchived() { + $mode = $this->getMode(); + return (bool)($mode & FileInfo::MODE_ARCHIVE); + } +} diff --git a/apps/files_external/3rdparty/icewind/smb/src/NativeServer.php b/apps/files_external/3rdparty/icewind/smb/src/NativeServer.php new file mode 100644 index 00000000000..4628e3ec108 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/NativeServer.php @@ -0,0 +1,60 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB; + +class NativeServer extends Server { + /** + * @var \Icewind\SMB\NativeState + */ + protected $state; + + /** + * @param string $host + * @param string $user + * @param string $password + */ + public function __construct($host, $user, $password) { + parent::__construct($host, $user, $password); + $this->state = new NativeState(); + } + + protected function connect() { + $user = $this->getUser(); + $workgroup = null; + if (strpos($user, '/')) { + list($workgroup, $user) = explode($user, '/'); + } + $this->state->init($workgroup, $user, $this->getPassword()); + } + + /** + * @return \Icewind\SMB\IShare[] + * @throws \Icewind\SMB\Exception\AuthenticationException + * @throws \Icewind\SMB\Exception\InvalidHostException + */ + public function listShares() { + $this->connect(); + $shares = array(); + $dh = $this->state->opendir('smb://' . $this->getHost()); + while ($share = $this->state->readdir($dh)) { + if ($share['type'] === 'file share') { + $shares[] = $this->getShare($share['name']); + } + } + $this->state->closedir($dh); + return $shares; + } + + /** + * @param string $name + * @return \Icewind\SMB\IShare + */ + public function getShare($name) { + return new NativeShare($this, $name); + } +} diff --git a/apps/files_external/3rdparty/icewind/smb/src/NativeShare.php b/apps/files_external/3rdparty/icewind/smb/src/NativeShare.php new file mode 100644 index 00000000000..c84e9611667 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/NativeShare.php @@ -0,0 +1,288 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB; + +class NativeShare implements IShare { + /** + * @var Server $server + */ + private $server; + + /** + * @var string $name + */ + private $name; + + /** + * @var \Icewind\SMB\NativeState $state + */ + private $state; + + /** + * @param Server $server + * @param string $name + */ + public function __construct($server, $name) { + $this->server = $server; + $this->name = $name; + $this->state = new NativeState(); + } + + /** + * @throws \Icewind\SMB\Exception\ConnectionException + * @throws \Icewind\SMB\Exception\AuthenticationException + * @throws \Icewind\SMB\Exception\InvalidHostException + */ + protected function connect() { + if ($this->state and $this->state instanceof NativeShare) { + return; + } + + $user = $this->server->getUser(); + if (strpos($user, '/')) { + list($workgroup, $user) = explode('/', $user); + } elseif (strpos($user, '\\')) { + list($workgroup, $user) = explode('\\', $user); + } else { + $workgroup = null; + } + $this->state->init($workgroup, $user, $this->server->getPassword()); + } + + /** + * Get the name of the share + * + * @return string + */ + public function getName() { + return $this->name; + } + + private function buildUrl($path) { + $url = sprintf('smb://%s/%s', $this->server->getHost(), $this->name); + if ($path) { + $path = trim($path, '/'); + $url .= '/'; + $url .= implode('/', array_map('rawurlencode', explode('/', $path))); + } + return $url; + } + + /** + * List the content of a remote folder + * + * @param string $path + * @return \Icewind\SMB\IFileInfo[] + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\InvalidTypeException + */ + public function dir($path) { + $this->connect(); + $files = array(); + + $dh = $this->state->opendir($this->buildUrl($path)); + while ($file = $this->state->readdir($dh)) { + $name = $file['name']; + if ($name !== '.' and $name !== '..') { + $files [] = new NativeFileInfo($this, $path . '/' . $name, $name); + } + } + + $this->state->closedir($dh); + return $files; + } + + /** + * @param string $path + * @return \Icewind\SMB\IFileInfo[] + */ + public function stat($path) { + return new NativeFileInfo($this, $path, basename($path), $this->getStat($path)); + } + + public function getStat($path) { + $this->connect(); + return $this->state->stat($this->buildUrl($path)); + } + + /** + * Create a folder on the share + * + * @param string $path + * @return bool + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\AlreadyExistsException + */ + public function mkdir($path) { + $this->connect(); + return $this->state->mkdir($this->buildUrl($path)); + } + + /** + * Remove a folder on the share + * + * @param string $path + * @return bool + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\InvalidTypeException + */ + public function rmdir($path) { + $this->connect(); + return $this->state->rmdir($this->buildUrl($path)); + } + + /** + * Delete a file on the share + * + * @param string $path + * @return bool + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\InvalidTypeException + */ + public function del($path) { + return $this->state->unlink($this->buildUrl($path)); + } + + /** + * Rename a remote file + * + * @param string $from + * @param string $to + * @return bool + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\AlreadyExistsException + */ + public function rename($from, $to) { + $this->connect(); + return $this->state->rename($this->buildUrl($from), $this->buildUrl($to)); + } + + /** + * Upload a local file + * + * @param string $source local file + * @param string $target remove file + * @return bool + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\InvalidTypeException + */ + public function put($source, $target) { + $this->connect(); + $sourceHandle = fopen($source, 'rb'); + $targetHandle = $this->state->create($this->buildUrl($target)); + + while ($data = fread($sourceHandle, 4096)) { + $this->state->write($targetHandle, $data); + } + $this->state->close($targetHandle); + return true; + } + + /** + * Download a remote file + * + * @param string $source remove file + * @param string $target local file + * @return bool + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\InvalidTypeException + */ + public function get($source, $target) { + $this->connect(); + $sourceHandle = $this->state->open($this->buildUrl($source), 'r'); + $targetHandle = fopen($target, 'wb'); + + while ($data = $this->state->read($sourceHandle, 4096)) { + fwrite($targetHandle, $data); + } + $this->state->close($sourceHandle); + return true; + } + + /** + * Open a readable stream top a remote file + * + * @param string $source + * @return resource a read only stream with the contents of the remote file + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\InvalidTypeException + */ + public function read($source) { + $this->connect(); + $handle = $this->state->open($this->buildUrl($source), 'r'); + return NativeStream::wrap($this->state, $handle, 'r'); + } + + /** + * Open a readable stream top a remote file + * + * @param string $source + * @return resource a read only stream with the contents of the remote file + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\InvalidTypeException + */ + public function write($source) { + $this->connect(); + $handle = $this->state->create($this->buildUrl($source)); + return NativeStream::wrap($this->state, $handle, 'w'); + } + + /** + * Get extended attributes for the path + * + * @param string $path + * @param string $attribute attribute to get the info + * @return string the attribute value + */ + public function getAttribute($path, $attribute) { + $this->connect(); + + $result = $this->state->getxattr($this->buildUrl($path), $attribute); + return $result; + } + + /** + * Get extended attributes for the path + * + * @param string $path + * @param string $attribute attribute to get the info + * @param mixed $value + * @return string the attribute value + */ + public function setAttribute($path, $attribute, $value) { + $this->connect(); + + if ($attribute === 'system.dos_attr.mode' and is_int($value)) { + $value = '0x' . dechex($value); + } + + return $this->state->setxattr($this->buildUrl($path), $attribute, $value); + } + + /** + * @param string $path + * @param int $mode a combination of FileInfo::MODE_READONLY, FileInfo::MODE_ARCHIVE, FileInfo::MODE_SYSTEM and FileInfo::MODE_HIDDEN, FileInfo::NORMAL + * @return mixed + */ + public function setMode($path, $mode) { + return $this->setAttribute($path, 'system.dos_attr.mode', $mode); + } + + public function __destruct() { + unset($this->state); + } +} diff --git a/apps/files_external/3rdparty/icewind/smb/src/NativeState.php b/apps/files_external/3rdparty/icewind/smb/src/NativeState.php new file mode 100644 index 00000000000..e3e344d9e01 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/NativeState.php @@ -0,0 +1,308 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB; + +use Icewind\SMB\Exception\AlreadyExistsException; +use Icewind\SMB\Exception\ConnectionRefusedException; +use Icewind\SMB\Exception\Exception; +use Icewind\SMB\Exception\ForbiddenException; +use Icewind\SMB\Exception\HostDownException; +use Icewind\SMB\Exception\InvalidTypeException; +use Icewind\SMB\Exception\NoRouteToHostException; +use Icewind\SMB\Exception\NotEmptyException; +use Icewind\SMB\Exception\NotFoundException; +use Icewind\SMB\Exception\TimedOutException; + +/** + * Low level wrapper for libsmbclient-php for error handling + */ +class NativeState { + /** + * @var resource + */ + protected $state; + + protected $handlerSet = false; + + protected $connected = false; + + protected function handleError($path) { + $error = smbclient_state_errno($this->state); + switch ($error) { + // see error.h + case 0; + return; + case 1: + case 13: + throw new ForbiddenException($path, $error); + case 2: + throw new NotFoundException($path, $error); + case 17: + throw new AlreadyExistsException($path, $error); + case 20: + throw new InvalidTypeException($path, $error); + case 21: + throw new InvalidTypeException($path, $error); + case 39: + throw new NotEmptyException($path, $error); + case 110: + throw new TimedOutException($path, $error); + case 111: + throw new ConnectionRefusedException($path, $error); + case 112: + throw new HostDownException($path, $error); + case 113: + throw new NoRouteToHostException($path, $error); + default: + $message = 'Unknown error (' . $error . ')'; + if ($path) { + $message .= ' for ' . $path; + } + throw new Exception($message, $error); + } + } + + protected function testResult($result, $path) { + if ($result === false or $result === null) { + $this->handleError($path); + } + } + + /** + * @param string $workGroup + * @param string $user + * @param string $password + * @return bool + */ + public function init($workGroup, $user, $password) { + if ($this->connected) { + return true; + } + $this->state = smbclient_state_new(); + $result = @smbclient_state_init($this->state, $workGroup, $user, $password); + + $this->testResult($result, ''); + $this->connected = true; + return $result; + } + + /** + * @param string $uri + * @return resource + */ + public function opendir($uri) { + $result = @smbclient_opendir($this->state, $uri); + + $this->testResult($result, $uri); + return $result; + } + + /** + * @param resource $dir + * @return array + */ + public function readdir($dir) { + $result = @smbclient_readdir($this->state, $dir); + + $this->testResult($result, $dir); + return $result; + } + + /** + * @param $dir + * @return bool + */ + public function closedir($dir) { + $result = smbclient_closedir($this->state, $dir); + + $this->testResult($result, $dir); + return $result; + } + + /** + * @param string $old + * @param string $new + * @return bool + */ + public function rename($old, $new) { + $result = @smbclient_rename($this->state, $old, $this->state, $new); + + $this->testResult($result, $new); + return $result; + } + + /** + * @param string $uri + * @return bool + */ + public function unlink($uri) { + $result = @smbclient_unlink($this->state, $uri); + + $this->testResult($result, $uri); + return $result; + } + + /** + * @param string $uri + * @param int $mask + * @return bool + */ + public function mkdir($uri, $mask = 0777) { + $result = @smbclient_mkdir($this->state, $uri, $mask); + + $this->testResult($result, $uri); + return $result; + } + + /** + * @param string $uri + * @return bool + */ + public function rmdir($uri) { + $result = @smbclient_rmdir($this->state, $uri); + + $this->testResult($result, $uri); + return $result; + } + + /** + * @param string $uri + * @return array + */ + public function stat($uri) { + $result = @smbclient_stat($this->state, $uri); + + $this->testResult($result, $uri); + return $result; + } + + /** + * @param resource $file + * @return array + */ + public function fstat($file) { + $result = @smbclient_fstat($this->state, $file); + + $this->testResult($result, $file); + return $result; + } + + /** + * @param string $uri + * @param string $mode + * @param int $mask + * @return resource + */ + public function open($uri, $mode, $mask = 0666) { + $result = @smbclient_open($this->state, $uri, $mode, $mask); + + $this->testResult($result, $uri); + return $result; + } + + /** + * @param string $uri + * @param int $mask + * @return resource + */ + public function create($uri, $mask = 0666) { + $result = @smbclient_creat($this->state, $uri, $mask); + + $this->testResult($result, $uri); + return $result; + } + + /** + * @param resource $file + * @param int $bytes + * @return string + */ + public function read($file, $bytes) { + $result = @smbclient_read($this->state, $file, $bytes); + + $this->testResult($result, $file); + return $result; + } + + /** + * @param resource $file + * @param string $data + * @param int $length + * @return int + */ + public function write($file, $data, $length = null) { + $result = @smbclient_write($this->state, $file, $data, $length); + + $this->testResult($result, $file); + return $result; + } + + /** + * @param resource $file + * @param int $offset + * @param int $whence SEEK_SET | SEEK_CUR | SEEK_END + * @return int | bool new file offset as measured from the start of the file on success, false on failure. + */ + public function lseek($file, $offset, $whence = SEEK_SET) { + $result = @smbclient_lseek($this->state, $file, $offset, $whence); + + $this->testResult($result, $file); + return $result; + } + + /** + * @param resource $file + * @param int $size + * @return bool + */ + public function ftruncate($file, $size) { + $result = @smbclient_ftruncate($this->state, $file, $size); + + $this->testResult($result, $file); + return $result; + } + + public function close($file) { + $result = @smbclient_close($this->state, $file); + + $this->testResult($result, $file); + return $result; + } + + /** + * @param string $uri + * @param string $key + * @return string + */ + public function getxattr($uri, $key) { + $result = @smbclient_getxattr($this->state, $uri, $key); + + $this->testResult($result, $uri); + return $result; + } + + /** + * @param string $uri + * @param string $key + * @param string $value + * @param int $flags + * @return mixed + */ + public function setxattr($uri, $key, $value, $flags = 0) { + $result = @smbclient_setxattr($this->state, $uri, $key, $value, $flags); + + $this->testResult($result, $uri); + return $result; + } + + public function __destruct() { + if ($this->connected) { + smbclient_state_free($this->state); + } + } +} diff --git a/apps/files_external/3rdparty/icewind/smb/src/NativeStream.php b/apps/files_external/3rdparty/icewind/smb/src/NativeStream.php new file mode 100644 index 00000000000..07bd2f1e797 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/NativeStream.php @@ -0,0 +1,114 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB; + +use Icewind\SMB\Exception\InvalidRequestException; +use Icewind\Streams\File; + +class NativeStream implements File { + /** + * @var resource + */ + public $context; + + /** + * @var \Icewind\SMB\NativeState + */ + private $state; + + /** + * @var resource + */ + private $handle; + + /** + * @var bool + */ + private $eof = false; + + /** + * Wrap a stream from libsmbclient-php into a regular php stream + * + * @param \Icewind\SMB\NativeState $state + * @param resource $smbStream + * @param string $mode + * @return resource + */ + public static function wrap($state, $smbStream, $mode) { + stream_wrapper_register('nativesmb', '\Icewind\SMB\NativeStream'); + $context = stream_context_create(array( + 'nativesmb' => array( + 'state' => $state, + 'handle' => $smbStream + ) + )); + $fh = fopen('nativesmb://', $mode, false, $context); + stream_wrapper_unregister('nativesmb'); + return $fh; + } + + public function stream_close() { + return $this->state->close($this->handle); + } + + public function stream_eof() { + return $this->eof; + } + + public function stream_flush() { + } + + + public function stream_open($path, $mode, $options, &$opened_path) { + $context = stream_context_get_options($this->context); + $this->state = $context['nativesmb']['state']; + $this->handle = $context['nativesmb']['handle']; + return true; + } + + public function stream_read($count) { + $result = $this->state->read($this->handle, $count); + if (strlen($result) < $count) { + $this->eof = true; + } + return $result; + } + + public function stream_seek($offset, $whence = SEEK_SET) { + $this->eof = false; + try { + return $this->state->lseek($this->handle, $offset, $whence) !== false; + } catch (InvalidRequestException $e) { + return false; + } + } + + public function stream_stat() { + return $this->state->fstat($this->handle); + } + + public function stream_tell() { + return $this->state->lseek($this->handle, 0, SEEK_CUR); + } + + public function stream_write($data) { + return $this->state->write($this->handle, $data); + } + + public function stream_truncate($size) { + return $this->state->ftruncate($this->handle, $size); + } + + public function stream_set_option($option, $arg1, $arg2) { + return false; + } + + public function stream_lock($operation) { + return false; + } +} diff --git a/apps/files_external/3rdparty/icewind/smb/src/Parser.php b/apps/files_external/3rdparty/icewind/smb/src/Parser.php new file mode 100644 index 00000000000..645678d9855 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/Parser.php @@ -0,0 +1,130 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB; + +use Icewind\SMB\Exception\AccessDeniedException; +use Icewind\SMB\Exception\AlreadyExistsException; +use Icewind\SMB\Exception\Exception; +use Icewind\SMB\Exception\FileInUseException; +use Icewind\SMB\Exception\InvalidTypeException; +use Icewind\SMB\Exception\NotEmptyException; +use Icewind\SMB\Exception\NotFoundException; + +class Parser { + /** + * @var string + */ + protected $timeZone; + + /** + * @param string $timeZone + */ + public function __construct($timeZone) { + $this->timeZone = $timeZone; + } + + public function checkForError($output, $path) { + if (count($output) === 0) { + return true; + } else { + if (strpos($output[0], 'does not exist')) { + throw new NotFoundException($path); + } + $parts = explode(' ', $output[0]); + $error = false; + foreach ($parts as $part) { + if (substr($part, 0, 9) === 'NT_STATUS') { + $error = $part; + } + } + switch ($error) { + case ErrorCodes::PathNotFound: + case ErrorCodes::ObjectNotFound: + case ErrorCodes::NoSuchFile: + throw new NotFoundException($path); + case ErrorCodes::NameCollision: + throw new AlreadyExistsException($path); + case ErrorCodes::AccessDenied: + throw new AccessDeniedException($path); + case ErrorCodes::DirectoryNotEmpty: + throw new NotEmptyException($path); + case ErrorCodes::FileIsADirectory: + case ErrorCodes::NotADirectory: + throw new InvalidTypeException($path); + case ErrorCodes::SharingViolation: + throw new FileInUseException($path); + default: + $message = 'Unknown error (' . $error . ')'; + if ($path) { + $message .= ' for ' . $path; + } + throw new Exception($message); + } + } + } + + public function parseMode($mode) { + $result = 0; + $modeStrings = array( + 'R' => FileInfo::MODE_READONLY, + 'H' => FileInfo::MODE_HIDDEN, + 'S' => FileInfo::MODE_SYSTEM, + 'D' => FileInfo::MODE_DIRECTORY, + 'A' => FileInfo::MODE_ARCHIVE, + 'N' => FileInfo::MODE_NORMAL + ); + foreach ($modeStrings as $char => $val) { + if (strpos($mode, $char) !== false) { + $result |= $val; + } + } + return $result; + } + + public function parseStat($output) { + $mtime = 0; + $mode = 0; + $size = 0; + foreach ($output as $line) { + list($name, $value) = explode(':', $line, 2); + $value = trim($value); + if ($name === 'write_time') { + $mtime = strtotime($value); + } else if ($name === 'attributes') { + $mode = hexdec(substr($value, 1, -1)); + } else if ($name === 'stream') { + list(, $size,) = explode(' ', $value); + $size = intval($size); + } + } + return array( + 'mtime' => $mtime, + 'mode' => $mode, + 'size' => $size + ); + } + + public function parseDir($output, $basePath) { + //last line is used space + array_pop($output); + $regex = '/^\s*(.*?)\s\s\s\s+(?:([NDHARS]*)\s+)?([0-9]+)\s+(.*)$/'; + //2 spaces, filename, optional type, size, date + $content = array(); + foreach ($output as $line) { + if (preg_match($regex, $line, $matches)) { + list(, $name, $mode, $size, $time) = $matches; + if ($name !== '.' and $name !== '..') { + $mode = $this->parseMode($mode); + $time = strtotime($time . ' ' . $this->timeZone); + $content[] = new FileInfo($basePath . '/' . $name, $name, $size, $time, $mode); + } + } + } + return $content; + } +} diff --git a/apps/files_external/3rdparty/icewind/smb/src/RawConnection.php b/apps/files_external/3rdparty/icewind/smb/src/RawConnection.php new file mode 100644 index 00000000000..926ce3714cf --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/RawConnection.php @@ -0,0 +1,165 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB; + +use Icewind\SMB\Exception\ConnectionException; + +class RawConnection { + /** + * @var string + */ + private $command; + + /** + * @var string[] + */ + private $env; + + /** + * @var resource[] $pipes + * + * $pipes[0] holds STDIN for smbclient + * $pipes[1] holds STDOUT for smbclient + */ + private $pipes; + + /** + * @var resource $process + */ + private $process; + + public function __construct($command, $env = array()) { + $this->command = $command; + $this->env = $env; + $this->connect(); + } + + private function connect() { + $descriptorSpec = array( + 0 => array('pipe', 'r'), // child reads from stdin + 1 => array('pipe', 'w'), // child writes to stdout + 2 => array('pipe', 'w'), // child writes to stderr + 3 => array('pipe', 'r'), // child reads from fd#3 + 4 => array('pipe', 'r'), // child reads from fd#4 + 5 => array('pipe', 'w') // child writes to fd#5 + ); + setlocale(LC_ALL, Server::LOCALE); + $env = array_merge($this->env, array( + 'CLI_FORCE_INTERACTIVE' => 'y', // Needed or the prompt isn't displayed!! + 'LC_ALL' => Server::LOCALE, + 'LANG' => Server::LOCALE, + 'COLUMNS' => 8192 // prevent smbclient from line-wrapping it's output + )); + $this->process = proc_open($this->command, $descriptorSpec, $this->pipes, '/', $env); + if (!$this->isValid()) { + throw new ConnectionException(); + } + } + + /** + * check if the connection is still active + * + * @return bool + */ + public function isValid() { + if (is_resource($this->process)) { + $status = proc_get_status($this->process); + return $status['running']; + } else { + return false; + } + } + + /** + * send input to the process + * + * @param string $input + */ + public function write($input) { + fwrite($this->getInputStream(), $input); + fflush($this->getInputStream()); + } + + /** + * read a line of output + * + * @return string + */ + public function readLine() { + return stream_get_line($this->getOutputStream(), 4086, "\n"); + } + + /** + * get all output until the process closes + * + * @return array + */ + public function readAll() { + $output = array(); + while ($line = $this->readLine()) { + $output[] = $line; + } + return $output; + } + + public function getInputStream() { + return $this->pipes[0]; + } + + public function getOutputStream() { + return $this->pipes[1]; + } + + public function getErrorStream() { + return $this->pipes[2]; + } + + public function getAuthStream() { + return $this->pipes[3]; + } + + public function getFileInputStream() { + return $this->pipes[4]; + } + + public function getFileOutputStream() { + return $this->pipes[5]; + } + + public function writeAuthentication($user, $password) { + $auth = ($password === false) + ? "username=$user" + : "username=$user\npassword=$password"; + + if (fwrite($this->getAuthStream(), $auth) === false) { + fclose($this->getAuthStream()); + return false; + } + fclose($this->getAuthStream()); + return true; + } + + public function close($terminate = true) { + if (!is_resource($this->process)) { + return; + } + if ($terminate) { + proc_terminate($this->process); + } + proc_close($this->process); + } + + public function reconnect() { + $this->close(); + $this->connect(); + } + + public function __destruct() { + $this->close(); + } +} diff --git a/apps/files_external/3rdparty/icewind/smb/src/Server.php b/apps/files_external/3rdparty/icewind/smb/src/Server.php new file mode 100644 index 00000000000..f7227d4baef --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/Server.php @@ -0,0 +1,141 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB; + +use Icewind\SMB\Exception\AuthenticationException; +use Icewind\SMB\Exception\InvalidHostException; + +class Server { + const CLIENT = 'smbclient'; + const LOCALE = 'en_US.UTF-8'; + + /** + * @var string $host + */ + protected $host; + + /** + * @var string $user + */ + protected $user; + + /** + * @var string $password + */ + protected $password; + + /** + * Check if the smbclient php extension is available + * + * @return bool + */ + public static function NativeAvailable() { + return function_exists('smbclient_state_new'); + } + + /** + * @param string $host + * @param string $user + * @param string $password + */ + public function __construct($host, $user, $password) { + $this->host = $host; + $this->user = $user; + $this->password = $password; + } + + /** + * @return string + */ + public function getAuthString() { + return $this->user . '%' . $this->password; + } + + /** + * @return string + */ + public function getUser() { + return $this->user; + } + + /** + * @return string + */ + public function getPassword() { + return $this->password; + } + + /** + * return string + */ + public function getHost() { + return $this->host; + } + + /** + * @return \Icewind\SMB\IShare[] + * + * @throws \Icewind\SMB\Exception\AuthenticationException + * @throws \Icewind\SMB\Exception\InvalidHostException + */ + public function listShares() { + $command = Server::CLIENT . ' --authentication-file=/proc/self/fd/3' . + ' -gL ' . escapeshellarg($this->getHost()); + $connection = new RawConnection($command); + $connection->writeAuthentication($this->getUser(), $this->getPassword()); + $output = $connection->readAll(); + + $line = $output[0]; + + $line = rtrim($line, ')'); + if (substr($line, -23) === ErrorCodes::LogonFailure) { + throw new AuthenticationException(); + } + if (substr($line, -26) === ErrorCodes::BadHostName) { + throw new InvalidHostException(); + } + if (substr($line, -22) === ErrorCodes::Unsuccessful) { + throw new InvalidHostException(); + } + if (substr($line, -28) === ErrorCodes::ConnectionRefused) { + throw new InvalidHostException(); + } + + $shareNames = array(); + foreach ($output as $line) { + if (strpos($line, '|')) { + list($type, $name, $description) = explode('|', $line); + if (strtolower($type) === 'disk') { + $shareNames[$name] = $description; + } + } + } + + $shares = array(); + foreach ($shareNames as $name => $description) { + $shares[] = $this->getShare($name); + } + return $shares; + } + + /** + * @param string $name + * @return \Icewind\SMB\IShare + */ + public function getShare($name) { + return new Share($this, $name); + } + + /** + * @return string + */ + public function getTimeZone() { + $command = 'net time zone -S ' . escapeshellarg($this->getHost()); + return exec($command); + } +} diff --git a/apps/files_external/3rdparty/icewind/smb/src/Share.php b/apps/files_external/3rdparty/icewind/smb/src/Share.php new file mode 100644 index 00000000000..5c48b1702fa --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/src/Share.php @@ -0,0 +1,394 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB; + +use Icewind\SMB\Exception\AccessDeniedException; +use Icewind\SMB\Exception\AlreadyExistsException; +use Icewind\SMB\Exception\ConnectionException; +use Icewind\SMB\Exception\Exception; +use Icewind\SMB\Exception\FileInUseException; +use Icewind\SMB\Exception\InvalidTypeException; +use Icewind\SMB\Exception\NotEmptyException; +use Icewind\SMB\Exception\NotFoundException; +use Icewind\Streams\CallbackWrapper; + +class Share implements IShare { + /** + * @var Server $server + */ + private $server; + + /** + * @var string $name + */ + private $name; + + /** + * @var Connection $connection + */ + public $connection; + + /** + * @var \Icewind\SMB\Parser + */ + protected $parser; + + private $serverTimezone; + + /** + * @param Server $server + * @param string $name + */ + public function __construct($server, $name) { + $this->server = $server; + $this->name = $name; + $this->parser = new Parser($this->server->getTimeZone()); + } + + /** + * @throws \Icewind\SMB\Exception\ConnectionException + * @throws \Icewind\SMB\Exception\AuthenticationException + * @throws \Icewind\SMB\Exception\InvalidHostException + */ + protected function connect() { + if ($this->connection and $this->connection->isValid()) { + return; + } + $command = sprintf('%s --authentication-file=/proc/self/fd/3 //%s/%s', + Server::CLIENT, + $this->server->getHost(), + $this->name + ); + $this->connection = new Connection($command); + $this->connection->writeAuthentication($this->server->getUser(), $this->server->getPassword()); + if (!$this->connection->isValid()) { + throw new ConnectionException(); + } + } + + protected function reconnect() { + $this->connection->reconnect(); + $this->connection->writeAuthentication($this->server->getUser(), $this->server->getPassword()); + if (!$this->connection->isValid()) { + throw new ConnectionException(); + } + } + + /** + * Get the name of the share + * + * @return string + */ + public function getName() { + return $this->name; + } + + protected function simpleCommand($command, $path) { + $path = $this->escapePath($path); + $cmd = $command . ' ' . $path; + $output = $this->execute($cmd); + return $this->parseOutput($output, $path); + } + + /** + * List the content of a remote folder + * + * @param $path + * @return \Icewind\SMB\IFileInfo[] + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\InvalidTypeException + */ + public function dir($path) { + $escapedPath = $this->escapePath($path); + $output = $this->execute('cd ' . $escapedPath); + //check output for errors + $this->parseOutput($output, $path); + $output = $this->execute('dir'); + $this->execute('cd /'); + + return $this->parser->parseDir($output, $path); + } + + /** + * @param string $path + * @return \Icewind\SMB\IFileInfo[] + */ + public function stat($path) { + $escapedPath = $this->escapePath($path); + $output = $this->execute('allinfo ' . $escapedPath); + if (count($output) < 3) { + $this->parseOutput($output, $path); + } + $stat = $this->parser->parseStat($output); + return new FileInfo($path, basename($path), $stat['size'], $stat['mtime'], $stat['mode']); + } + + /** + * Create a folder on the share + * + * @param string $path + * @return bool + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\AlreadyExistsException + */ + public function mkdir($path) { + return $this->simpleCommand('mkdir', $path); + } + + /** + * Remove a folder on the share + * + * @param string $path + * @return bool + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\InvalidTypeException + */ + public function rmdir($path) { + return $this->simpleCommand('rmdir', $path); + } + + /** + * Delete a file on the share + * + * @param string $path + * @param bool $secondTry + * @return bool + * @throws InvalidTypeException + * @throws NotFoundException + * @throws \Exception + */ + public function del($path, $secondTry = false) { + //del return a file not found error when trying to delete a folder + //we catch it so we can check if $path doesn't exist or is of invalid type + try { + return $this->simpleCommand('del', $path); + } catch (NotFoundException $e) { + //no need to do anything with the result, we just check if this throws the not found error + try { + $this->simpleCommand('ls', $path); + } catch (NotFoundException $e2) { + throw $e; + } catch (\Exception $e2) { + throw new InvalidTypeException($path); + } + throw $e; + } catch (FileInUseException $e) { + if ($secondTry) { + throw $e; + } + $this->reconnect(); + return $this->del($path, true); + } + } + + /** + * Rename a remote file + * + * @param string $from + * @param string $to + * @return bool + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\AlreadyExistsException + */ + public function rename($from, $to) { + $path1 = $this->escapePath($from); + $path2 = $this->escapePath($to); + $cmd = 'rename ' . $path1 . ' ' . $path2; + $output = $this->execute($cmd); + return $this->parseOutput($output, $to); + } + + /** + * Upload a local file + * + * @param string $source local file + * @param string $target remove file + * @return bool + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\InvalidTypeException + */ + public function put($source, $target) { + $path1 = $this->escapeLocalPath($source); //first path is local, needs different escaping + $path2 = $this->escapePath($target); + $output = $this->execute('put ' . $path1 . ' ' . $path2); + return $this->parseOutput($output, $target); + } + + /** + * Download a remote file + * + * @param string $source remove file + * @param string $target local file + * @return bool + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\InvalidTypeException + */ + public function get($source, $target) { + $path1 = $this->escapePath($source); + $path2 = $this->escapeLocalPath($target); //second path is local, needs different escaping + $output = $this->execute('get ' . $path1 . ' ' . $path2); + return $this->parseOutput($output, $source); + } + + /** + * Open a readable stream to a remote file + * + * @param string $source + * @return resource a read only stream with the contents of the remote file + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\InvalidTypeException + */ + public function read($source) { + $source = $this->escapePath($source); + // close the single quote, open a double quote where we put the single quote... + $source = str_replace('\'', '\'"\'"\'', $source); + // since returned stream is closed by the caller we need to create a new instance + // since we can't re-use the same file descriptor over multiple calls + $command = sprintf('%s --authentication-file=/proc/self/fd/3 //%s/%s -c \'get %s /proc/self/fd/5\'', + Server::CLIENT, + $this->server->getHost(), + $this->name, + $source + ); + $connection = new Connection($command); + $connection->writeAuthentication($this->server->getUser(), $this->server->getPassword()); + $fh = $connection->getFileOutputStream(); + stream_context_set_option($fh, 'file', 'connection', $connection); + return $fh; + } + + /** + * Open a writable stream to a remote file + * + * @param string $target + * @return resource a write only stream to upload a remote file + * + * @throws \Icewind\SMB\Exception\NotFoundException + * @throws \Icewind\SMB\Exception\InvalidTypeException + */ + public function write($target) { + $target = $this->escapePath($target); + // close the single quote, open a double quote where we put the single quote... + $target = str_replace('\'', '\'"\'"\'', $target); + // since returned stream is closed by the caller we need to create a new instance + // since we can't re-use the same file descriptor over multiple calls + $command = sprintf('%s --authentication-file=/proc/self/fd/3 //%s/%s -c \'put /proc/self/fd/4 %s\'', + Server::CLIENT, + $this->server->getHost(), + $this->name, + $target + ); + $connection = new RawConnection($command); + $connection->writeAuthentication($this->server->getUser(), $this->server->getPassword()); + $fh = $connection->getFileInputStream(); + + // use a close callback to ensure the upload is finished before continuing + // this also serves as a way to keep the connection in scope + return CallbackWrapper::wrap($fh, null, null, function () use ($connection) { + $connection->close(false); // dont terminate, give the upload some time + }); + } + + /** + * @param string $path + * @param int $mode a combination of FileInfo::MODE_READONLY, FileInfo::MODE_ARCHIVE, FileInfo::MODE_SYSTEM and FileInfo::MODE_HIDDEN, FileInfo::NORMAL + * @return mixed + */ + public function setMode($path, $mode) { + $modeString = ''; + $modeMap = array( + FileInfo::MODE_READONLY => 'r', + FileInfo::MODE_HIDDEN => 'h', + FileInfo::MODE_ARCHIVE => 'a', + FileInfo::MODE_SYSTEM => 's' + ); + foreach ($modeMap as $modeByte => $string) { + if ($mode & $modeByte) { + $modeString .= $string; + } + } + $path = $this->escapePath($path); + + // first reset the mode to normal + $cmd = 'setmode ' . $path . ' -rsha'; + $output = $this->execute($cmd); + $this->parseOutput($output, $path); + + // then set the modes we want + $cmd = 'setmode ' . $path . ' ' . $modeString; + $output = $this->execute($cmd); + return $this->parseOutput($output, $path); + } + + /** + * @param string $command + * @return array + */ + protected function execute($command) { + $this->connect(); + $this->connection->write($command . PHP_EOL); + $output = $this->connection->read(); + return $output; + } + + /** + * check output for errors + * + * @param string[] $lines + * @param string $path + * + * @throws NotFoundException + * @throws \Icewind\SMB\Exception\AlreadyExistsException + * @throws \Icewind\SMB\Exception\AccessDeniedException + * @throws \Icewind\SMB\Exception\NotEmptyException + * @throws \Icewind\SMB\Exception\InvalidTypeException + * @throws \Icewind\SMB\Exception\Exception + * @return bool + */ + protected function parseOutput($lines, $path = '') { + $this->parser->checkForError($lines, $path); + } + + /** + * @param string $string + * @return string + */ + protected function escape($string) { + return escapeshellarg($string); + } + + /** + * @param string $path + * @return string + */ + protected function escapePath($path) { + $path = str_replace('/', '\\', $path); + $path = str_replace('"', '^"', $path); + return '"' . $path . '"'; + } + + /** + * @param string $path + * @return string + */ + protected function escapeLocalPath($path) { + $path = str_replace('"', '\"', $path); + return '"' . $path . '"'; + } + + public function __destruct() { + unset($this->connection); + } +} diff --git a/apps/files_external/3rdparty/icewind/smb/tests/AbstractShare.php b/apps/files_external/3rdparty/icewind/smb/tests/AbstractShare.php new file mode 100644 index 00000000000..117fff1ca19 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/tests/AbstractShare.php @@ -0,0 +1,534 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Test; + +use Icewind\SMB\FileInfo; + +abstract class AbstractShare extends \PHPUnit_Framework_TestCase { + /** + * @var \Icewind\SMB\Server $server + */ + protected $server; + + /** + * @var \Icewind\SMB\IShare $share + */ + protected $share; + + /** + * @var string $root + */ + protected $root; + + protected $config; + + public function tearDown() { + try { + if ($this->share) { + $this->cleanDir($this->root); + } + unset($this->share); + } catch (\Exception $e) { + unset($this->share); + throw $e; + } + } + + public function nameProvider() { + // / ? < > \ : * | " are illegal characters in path on windows + return array( + array('simple'), + array('with spaces_and-underscores'), + array("single'quote'"), + array('日本語'), + array('url %2F +encode'), + array('a somewhat longer filename than the other with more charaters as the all the other filenames'), + array('$as#d€££Ö€ßœĚęĘĞĜΣΥΦΩΫ') + ); + } + + public function fileDataProvider() { + return array( + array('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua'), + array('Mixed language, 日本語 が わからか and Various _/* characters \\|” €') + ); + } + + public function nameAndDataProvider() { + $names = $this->nameProvider(); + $data = $this->fileDataProvider(); + $result = array(); + foreach ($names as $name) { + foreach ($data as $text) { + $result[] = array($name[0], $text[0]); + } + } + return $result; + } + + public function cleanDir($dir) { + $content = $this->share->dir($dir); + foreach ($content as $metadata) { + if ($metadata->isDirectory()) { + $this->cleanDir($metadata->getPath()); + } else { + $this->share->del($metadata->getPath()); + } + } + $this->share->rmdir($dir); + } + + private function getTextFile($text = '') { + if (!$text) { + $text = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua'; + } + $file = tempnam('/tmp', 'smb_test_'); + file_put_contents($file, $text); + return $file; + } + + public function testListShares() { + $shares = $this->server->listShares(); + foreach ($shares as $share) { + if ($share->getName() === $this->config->share) { + return; + } + } + $this->fail('Share "' . $this->config->share . '" not found'); + } + + public function testRootStartsEmpty() { + $this->assertEquals(array(), $this->share->dir($this->root)); + } + + /** + * @dataProvider nameProvider + */ + public function testMkdir($name) { + $this->share->mkdir($this->root . '/' . $name); + $dirs = $this->share->dir($this->root); + $this->assertCount(1, $dirs); + $this->assertEquals($name, $dirs[0]->getName()); + $this->assertTrue($dirs[0]->isDirectory()); + } + + /** + * @dataProvider nameProvider + */ + public function testRenameDirectory($name) { + $this->share->mkdir($this->root . '/' . $name); + $this->share->rename($this->root . '/' . $name, $this->root . '/' . $name . '_rename'); + $dirs = $this->share->dir($this->root); + $this->assertEquals(1, count($dirs)); + $this->assertEquals($name . '_rename', $dirs[0]->getName()); + } + + /** + * @dataProvider nameProvider + */ + public function testRmdir($name) { + $this->share->mkdir($this->root . '/' . $name); + $this->share->rmdir($this->root . '/' . $name); + $this->assertCount(0, $this->share->dir($this->root)); + } + + /** + * @dataProvider nameAndDataProvider + */ + public function testPut($name, $text) { + $tmpFile = $this->getTextFile($text); + $size = filesize($tmpFile); + + $this->share->put($tmpFile, $this->root . '/' . $name); + unlink($tmpFile); + + $files = $this->share->dir($this->root); + $this->assertCount(1, $files); + $this->assertEquals($name, $files[0]->getName()); + $this->assertEquals($size, $files[0]->getSize()); + $this->assertFalse($files[0]->isDirectory()); + } + + /** + * @dataProvider nameProvider + */ + public function testRenameFile($name) { + $tmpFile = $this->getTextFile(); + + $this->share->put($tmpFile, $this->root . '/' . $name); + unlink($tmpFile); + + $this->share->rename($this->root . '/' . $name, $this->root . '/' . $name . '_renamed'); + + $files = $this->share->dir($this->root); + $this->assertEquals(1, count($files)); + $this->assertEquals($name . '_renamed', $files[0]->getName()); + } + + /** + * @dataProvider nameAndDataProvider + */ + public function testGet($name, $text) { + $tmpFile = $this->getTextFile($text); + + $this->share->put($tmpFile, $this->root . '/' . $name); + unlink($tmpFile); + + $targetFile = tempnam('/tmp', 'smb_test_'); + $this->share->get($this->root . '/' . $name, $targetFile); + + $this->assertEquals($text, file_get_contents($targetFile)); + unlink($targetFile); + } + + /** + * @dataProvider nameProvider + */ + public function testDel($name) { + $tmpFile = $this->getTextFile(); + + $this->share->put($tmpFile, $this->root . '/' . $name); + unlink($tmpFile); + + $this->share->del($this->root . '/' . $name); + $this->assertCount(0, $this->share->dir($this->root)); + } + + /** + * @expectedException \Icewind\SMB\Exception\NotFoundException + */ + public function testCreateFolderInNonExistingFolder() { + $this->share->mkdir($this->root . '/foo/bar'); + } + + /** + * @expectedException \Icewind\SMB\Exception\NotFoundException + */ + public function testRemoveFolderInNonExistingFolder() { + $this->share->rmdir($this->root . '/foo/bar'); + } + + /** + * @expectedException \Icewind\SMB\Exception\NotFoundException + */ + public function testRemoveNonExistingFolder() { + $this->share->rmdir($this->root . '/foo'); + } + + /** + * @expectedException \Icewind\SMB\Exception\AlreadyExistsException + */ + public function testCreateExistingFolder() { + $this->share->mkdir($this->root . '/bar'); + $this->share->mkdir($this->root . '/bar'); + $this->share->rmdir($this->root . '/bar'); + } + + /** + * @expectedException \Icewind\SMB\Exception\InvalidTypeException + */ + public function testCreateFileExistingFolder() { + $this->share->mkdir($this->root . '/bar'); + $this->share->put($this->getTextFile(), $this->root . '/bar'); + $this->share->rmdir($this->root . '/bar'); + } + + /** + * @expectedException \Icewind\SMB\Exception\NotFoundException + */ + public function testCreateFileInNonExistingFolder() { + $this->share->put($this->getTextFile(), $this->root . '/foo/bar'); + } + + /** + * @expectedException \Icewind\SMB\Exception\NotFoundException + */ + public function testTestRemoveNonExistingFile() { + $this->share->del($this->root . '/foo'); + } + + /** + * @expectedException \Icewind\SMB\Exception\NotFoundException + */ + public function testDownloadNonExistingFile() { + $this->share->get($this->root . '/foo', '/dev/null'); + } + + /** + * @expectedException \Icewind\SMB\Exception\InvalidTypeException + */ + public function testDownloadFolder() { + $this->share->mkdir($this->root . '/foobar'); + $this->share->get($this->root . '/foobar', '/dev/null'); + $this->share->rmdir($this->root . '/foobar'); + } + + /** + * @expectedException \Icewind\SMB\Exception\InvalidTypeException + */ + public function testDelFolder() { + $this->share->mkdir($this->root . '/foobar'); + $this->share->del($this->root . '/foobar'); + $this->share->rmdir($this->root . '/foobar'); + } + + /** + * @expectedException \Icewind\SMB\Exception\InvalidTypeException + */ + public function testRmdirFile() { + $this->share->put($this->getTextFile(), $this->root . '/foobar'); + $this->share->rmdir($this->root . '/foobar'); + $this->share->del($this->root . '/foobar'); + } + + /** + * @expectedException \Icewind\SMB\Exception\NotEmptyException + */ + public function testRmdirNotEmpty() { + $this->share->mkdir($this->root . '/foobar'); + $this->share->put($this->getTextFile(), $this->root . '/foobar/asd'); + $this->share->rmdir($this->root . '/foobar'); + } + + /** + * @expectedException \Icewind\SMB\Exception\NotFoundException + */ + public function testDirNonExisting() { + $this->share->dir('/foobar/asd'); + } + + /** + * @expectedException \Icewind\SMB\Exception\NotFoundException + */ + public function testRmDirNonExisting() { + $this->share->rmdir('/foobar/asd'); + } + + /** + * @expectedException \Icewind\SMB\Exception\NotFoundException + */ + public function testRenameNonExisting() { + $this->share->rename('/foobar/asd', '/foobar/bar'); + } + + /** + * @expectedException \Icewind\SMB\Exception\NotFoundException + */ + public function testRenameTargetNonExisting() { + $txt = $this->getTextFile(); + $this->share->put($txt, $this->root . '/foo.txt'); + unlink($txt); + $this->share->rename($this->root . '/foo.txt', $this->root . '/bar/foo.txt'); + } + + public function testModifiedDate() { + $now = time(); + $this->share->put($this->getTextFile(), $this->root . '/foo.txt'); + $dir = $this->share->dir($this->root); + $mtime = $dir[0]->getMTime(); + $this->assertTrue(abs($now - $mtime) <= 1, 'Modified time differs by ' . abs($now - $mtime) . ' seconds'); + $this->share->del($this->root . '/foo.txt'); + } + + /** + * @dataProvider nameAndDataProvider + */ + public function testReadStream($name, $text) { + $sourceFile = $this->getTextFile($text); + $this->share->put($sourceFile, $this->root . '/' . $name); + $fh = $this->share->read($this->root . '/' . $name); + $content = stream_get_contents($fh); + fclose($fh); + $this->share->del($this->root . '/' . $name); + + $this->assertEquals(file_get_contents($sourceFile), $content); + } + + /** + * @dataProvider nameAndDataProvider + */ + public function testWriteStream($name, $text) { + $fh = $this->share->write($this->root . '/' . $name); + fwrite($fh, $text); + fclose($fh); + + $tmpFile1 = tempnam('/tmp', 'smb_test_'); + $this->share->get($this->root . '/' . $name, $tmpFile1); + $this->assertEquals($text, file_get_contents($tmpFile1)); + $this->share->del($this->root . '/' . $name); + unlink($tmpFile1); + } + + public function testDir() { + $txtFile = $this->getTextFile(); + + $this->share->mkdir($this->root . '/dir'); + $this->share->put($txtFile, $this->root . '/file.txt'); + unlink($txtFile); + + $dir = $this->share->dir($this->root); + if ($dir[0]->getName() === 'dir') { + $dirEntry = $dir[0]; + } else { + $dirEntry = $dir[1]; + } + $this->assertTrue($dirEntry->isDirectory()); + $this->assertFalse($dirEntry->isReadOnly()); + $this->assertFalse($dirEntry->isReadOnly()); + + if ($dir[0]->getName() === 'file.txt') { + $fileEntry = $dir[0]; + } else { + $fileEntry = $dir[1]; + } + $this->assertFalse($fileEntry->isDirectory()); + $this->assertFalse($fileEntry->isReadOnly()); + $this->assertFalse($fileEntry->isReadOnly()); + } + + /** + * @dataProvider nameProvider + */ + public function testStat($name) { + $txtFile = $this->getTextFile(); + $size = filesize($txtFile); + + $this->share->put($txtFile, $this->root . '/' . $name); + unlink($txtFile); + + $info = $this->share->stat($this->root . '/' . $name); + $this->assertEquals($size, $info->getSize()); + } + + /** + * @expectedException \Icewind\SMB\Exception\NotFoundException + */ + public function testStatNonExisting() { + $this->share->stat($this->root . '/fo.txt'); + } + + /** + * note setting archive and system bit is not supported + * + * @dataProvider nameProvider + */ + public function testSetMode($name) { + $txtFile = $this->getTextFile(); + + $this->share->put($txtFile, $this->root . '/' . $name); + + $this->share->setMode($this->root . '/' . $name, FileInfo::MODE_NORMAL); + $info = $this->share->stat($this->root . '/' . $name); + $this->assertFalse($info->isReadOnly()); + $this->assertFalse($info->isArchived()); + $this->assertFalse($info->isSystem()); + $this->assertFalse($info->isHidden()); + + $this->share->setMode($this->root . '/' . $name, FileInfo::MODE_READONLY); + $info = $this->share->stat($this->root . '/' . $name); + $this->assertTrue($info->isReadOnly()); + $this->assertFalse($info->isArchived()); + $this->assertFalse($info->isSystem()); + $this->assertFalse($info->isHidden()); + + $this->share->setMode($this->root . '/' . $name, FileInfo::MODE_ARCHIVE); + $info = $this->share->stat($this->root . '/' . $name); + $this->assertFalse($info->isReadOnly()); + $this->assertTrue($info->isArchived()); + $this->assertFalse($info->isSystem()); + $this->assertFalse($info->isHidden()); + + $this->share->setMode($this->root . '/' . $name, FileInfo::MODE_READONLY | FileInfo::MODE_ARCHIVE); + $info = $this->share->stat($this->root . '/' . $name); + $this->assertTrue($info->isReadOnly()); + $this->assertTrue($info->isArchived()); + $this->assertFalse($info->isSystem()); + $this->assertFalse($info->isHidden()); + + $this->share->setMode($this->root . '/' . $name, FileInfo::MODE_HIDDEN); + $info = $this->share->stat($this->root . '/' . $name); + $this->assertFalse($info->isReadOnly()); + $this->assertFalse($info->isArchived()); + $this->assertFalse($info->isSystem()); + $this->assertTrue($info->isHidden()); + + $this->share->setMode($this->root . '/' . $name, FileInfo::MODE_SYSTEM); + $info = $this->share->stat($this->root . '/' . $name); + $this->assertFalse($info->isReadOnly()); + $this->assertFalse($info->isArchived()); + $this->assertTrue($info->isSystem()); + $this->assertFalse($info->isHidden()); + + $this->share->setMode($this->root . '/' . $name, FileInfo::MODE_NORMAL); + $info = $this->share->stat($this->root . '/' . $name); + $this->assertFalse($info->isReadOnly()); + $this->assertFalse($info->isArchived()); + $this->assertFalse($info->isSystem()); + $this->assertFalse($info->isHidden()); + } + + public function pathProvider() { + // / ? < > \ : * | " are illegal characters in path on windows + return array( + array('dir/sub/foo.txt'), + array('bar.txt'), + array("single'quote'/sub/foo.txt"), + array('日本語/url %2F +encode/asd.txt'), + array( + 'a somewhat longer folder than the other with more charaters as the all the other filenames/' . + 'followed by a somewhat long file name after that.txt' + ) + ); + } + + /** + * @dataProvider pathProvider + */ + public function testSubDirs($path) { + $dirs = explode('/', $path); + $name = array_pop($dirs); + $fullPath = ''; + foreach ($dirs as $dir) { + $fullPath .= '/' . $dir; + $this->share->mkdir($this->root . $fullPath); + } + $txtFile = $this->getTextFile(); + $size = filesize($txtFile); + $this->share->put($txtFile, $this->root . $fullPath . '/' . $name); + unlink($txtFile); + $info = $this->share->stat($this->root . $fullPath . '/' . $name); + $this->assertEquals($size, $info->getSize()); + $this->assertFalse($info->isHidden()); + } + + public function testDelAfterStat() { + $name = 'foo.txt'; + $txtFile = $this->getTextFile(); + + $this->share->put($txtFile, $this->root . '/' . $name); + unlink($txtFile); + + $this->share->stat($this->root . '/' . $name); + $this->share->del($this->root . '/foo.txt'); + } + + /** + * @param $name + * @dataProvider nameProvider + */ + public function testDirPaths($name) { + $txtFile = $this->getTextFile(); + $this->share->mkdir($this->root . '/' . $name); + $this->share->put($txtFile, $this->root . '/' . $name . '/' . $name); + unlink($txtFile); + + $content = $this->share->dir($this->root . '/' . $name); + $this->assertCount(1, $content); + $this->assertEquals($name, $content[0]->getName()); + } +} diff --git a/apps/files_external/3rdparty/icewind/smb/tests/NativeShare.php b/apps/files_external/3rdparty/icewind/smb/tests/NativeShare.php new file mode 100644 index 00000000000..d8e10235c12 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/tests/NativeShare.php @@ -0,0 +1,27 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Test; + +use Icewind\SMB\NativeServer; + +class NativeShare extends AbstractShare { + public function setUp() { + if (!function_exists('smbclient_state_new')) { + $this->markTestSkipped('libsmbclient php extension not installed'); + } + $this->config = json_decode(file_get_contents(__DIR__ . '/config.json')); + $this->server = new NativeServer($this->config->host, $this->config->user, $this->config->password); + $this->share = $this->server->getShare($this->config->share); + if ($this->config->root) { + $this->root = '/' . $this->config->root . '/' . uniqid(); + } else { + $this->root = '/' . uniqid(); + } + $this->share->mkdir($this->root); + } +} diff --git a/apps/files_external/3rdparty/icewind/smb/tests/NativeStream.php b/apps/files_external/3rdparty/icewind/smb/tests/NativeStream.php new file mode 100644 index 00000000000..2d7b62fedeb --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/tests/NativeStream.php @@ -0,0 +1,143 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Test; + +use Icewind\SMB\NativeServer; + +class NativeStream extends \PHPUnit_Framework_TestCase { + /** + * @var \Icewind\SMB\Server $server + */ + protected $server; + + /** + * @var \Icewind\SMB\NativeShare $share + */ + protected $share; + + /** + * @var string $root + */ + protected $root; + + protected $config; + + public function setUp() { + if (!function_exists('smbclient_state_new')) { + $this->markTestSkipped('libsmbclient php extension not installed'); + } + $this->config = json_decode(file_get_contents(__DIR__ . '/config.json')); + $this->server = new NativeServer($this->config->host, $this->config->user, $this->config->password); + $this->share = $this->server->getShare($this->config->share); + if ($this->config->root) { + $this->root = '/' . $this->config->root . '/' . uniqid(); + } else { + $this->root = '/' . uniqid(); + } + $this->share->mkdir($this->root); + } + + private function getTextFile() { + $text = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua'; + $file = tempnam('/tmp', 'smb_test_'); + file_put_contents($file, $text); + return $file; + } + + public function testSeekTell() { + $sourceFile = $this->getTextFile(); + $this->share->put($sourceFile, $this->root . '/foobar'); + $fh = $this->share->read($this->root . '/foobar'); + $content = fread($fh, 3); + $this->assertEquals('Lor', $content); + + fseek($fh, -2, SEEK_CUR); + + $content = fread($fh, 3); + $this->assertEquals('ore', $content); + + fseek($fh, 3, SEEK_SET); + + $content = fread($fh, 3); + $this->assertEquals('em ', $content); + + fseek($fh, -3, SEEK_END); + + $content = fread($fh, 3); + $this->assertEquals('qua', $content); + + fseek($fh, -3, SEEK_END); + $this->assertEquals(120, ftell($fh)); + } + + public function testStat() { + $sourceFile = $this->getTextFile(); + $this->share->put($sourceFile, $this->root . '/foobar'); + $fh = $this->share->read($this->root . '/foobar'); + $stat = fstat($fh); + $this->assertEquals(filesize($sourceFile), $stat['size']); + unlink($sourceFile); + } + + public function testTruncate() { + if (version_compare(phpversion(), '5.4.0', '<')) { + $this->markTestSkipped('php <5.4 doesn\'t support truncate for stream wrappers'); + } + $fh = $this->share->write($this->root . '/foobar'); + fwrite($fh, 'foobar'); + ftruncate($fh, 3); + fclose($fh); + + $fh = $this->share->read($this->root . '/foobar'); + $this->assertEquals('foo', stream_get_contents($fh)); + } + + public function testEOF() { + if (version_compare(phpversion(), '5.4.0', '<')) { + $this->markTestSkipped('php <5.4 doesn\'t support truncate for stream wrappers'); + } + $fh = $this->share->write($this->root . '/foobar'); + fwrite($fh, 'foobar'); + fclose($fh); + + $fh = $this->share->read($this->root . '/foobar'); + fread($fh, 3); + $this->assertFalse(feof($fh)); + fread($fh, 5); + $this->assertTrue(feof($fh)); + } + + public function testLockUnsupported() { + $fh = $this->share->write($this->root . '/foobar'); + $this->assertFalse(flock($fh, LOCK_SH)); + } + + public function testSetOptionUnsupported() { + $fh = $this->share->write($this->root . '/foobar'); + $this->assertFalse(stream_set_blocking($fh, false)); + } + + public function tearDown() { + if ($this->share) { + $this->cleanDir($this->root); + } + unset($this->share); + } + + public function cleanDir($dir) { + $content = $this->share->dir($dir); + foreach ($content as $metadata) { + if ($metadata->isDirectory()) { + $this->cleanDir($metadata->getPath()); + } else { + $this->share->del($metadata->getPath()); + } + } + $this->share->rmdir($dir); + } +} diff --git a/apps/files_external/3rdparty/icewind/smb/tests/Parser.php b/apps/files_external/3rdparty/icewind/smb/tests/Parser.php new file mode 100644 index 00000000000..0dd06d6af33 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/tests/Parser.php @@ -0,0 +1,87 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Test; + + +use Icewind\SMB\FileInfo; + +class Parser extends \PHPUnit_Framework_TestCase { + public function modeProvider() { + return array( + array('D', FileInfo::MODE_DIRECTORY), + array('A', FileInfo::MODE_ARCHIVE), + array('S', FileInfo::MODE_SYSTEM), + array('H', FileInfo::MODE_HIDDEN), + array('R', FileInfo::MODE_READONLY), + array('N', FileInfo::MODE_NORMAL), + array('RA', FileInfo::MODE_READONLY | FileInfo::MODE_ARCHIVE), + array('RAH', FileInfo::MODE_READONLY | FileInfo::MODE_ARCHIVE | FileInfo::MODE_HIDDEN) + ); + } + + /** + * @dataProvider modeProvider + */ + public function testParseMode($string, $mode) { + $parser = new \Icewind\SMB\Parser('UTC'); + $this->assertEquals($mode, $parser->parseMode($string), 'Failed parsing ' . $string); + } + + public function statProvider() { + return array( + array( + array( + 'altname: test.txt', + 'create_time: Sat Oct 12 07:05:58 PM 2013 CEST', + 'access_time: Tue Oct 15 02:58:48 PM 2013 CEST', + 'write_time: Sat Oct 12 07:05:58 PM 2013 CEST', + 'change_time: Sat Oct 12 07:05:58 PM 2013 CEST', + 'attributes: (80)', + 'stream: [::$DATA], 29634 bytes' + ), + array( + 'mtime' => strtotime('12 Oct 2013 19:05:58 CEST'), + 'mode' => FileInfo::MODE_NORMAL, + 'size' => 29634 + )) + ); + } + + /** + * @dataProvider statProvider + */ + public function testStat($output, $stat) { + $parser = new \Icewind\SMB\Parser('UTC'); + $this->assertEquals($stat, $parser->parseStat($output)); + } + + public function dirProvider() { + return array( + array( + array( + ' . D 0 Tue Aug 26 19:11:56 2014', + ' .. DR 0 Sun Oct 28 15:24:02 2012', + ' c.pdf N 29634 Sat Oct 12 19:05:58 2013', + '', + ' 62536 blocks of size 8388608. 57113 blocks available' + ), + array( + new FileInfo('/c.pdf', 'c.pdf', 29634, strtotime('12 Oct 2013 19:05:58 CEST'), FileInfo::MODE_NORMAL) + ) + ) + ); + } + + /** + * @dataProvider dirProvider + */ + public function testDir($output, $dir) { + $parser = new \Icewind\SMB\Parser('CEST'); + $this->assertEquals($dir, $parser->parseDir($output, '')); + } +} diff --git a/apps/files_external/3rdparty/icewind/smb/tests/Server.php b/apps/files_external/3rdparty/icewind/smb/tests/Server.php new file mode 100644 index 00000000000..9f62886654f --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/tests/Server.php @@ -0,0 +1,57 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Test; + +class Server extends \PHPUnit_Framework_TestCase { + /** + * @var \Icewind\SMB\Server $server + */ + private $server; + + private $config; + + public function setUp() { + $this->config = json_decode(file_get_contents(__DIR__ . '/config.json')); + $this->server = new \Icewind\SMB\Server($this->config->host, $this->config->user, $this->config->password); + } + + public function testListShares() { + $shares = $this->server->listShares(); + foreach ($shares as $share) { + if ($share->getName() === $this->config->share) { + return; + } + } + $this->fail('Share "' . $this->config->share . '" not found'); + } + + /** + * @expectedException \Icewind\SMB\Exception\AuthenticationException + */ + public function testWrongUserName() { + $this->markTestSkipped('This fails for no reason on travis'); + $server = new \Icewind\SMB\Server($this->config->host, uniqid(), uniqid()); + $server->listShares(); + } + + /** + * @expectedException \Icewind\SMB\Exception\AuthenticationException + */ + public function testWrongPassword() { + $server = new \Icewind\SMB\Server($this->config->host, $this->config->user, uniqid()); + $server->listShares(); + } + + /** + * @expectedException \Icewind\SMB\Exception\InvalidHostException + */ + public function testWrongHost() { + $server = new \Icewind\SMB\Server(uniqid(), $this->config->user, $this->config->password); + $server->listShares(); + } +} diff --git a/apps/files_external/3rdparty/icewind/smb/tests/Share.php b/apps/files_external/3rdparty/icewind/smb/tests/Share.php new file mode 100644 index 00000000000..a629914d748 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/tests/Share.php @@ -0,0 +1,24 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\SMB\Test; + +use Icewind\SMB\Server as NormalServer; + +class Share extends AbstractShare { + public function setUp() { + $this->config = json_decode(file_get_contents(__DIR__ . '/config.json')); + $this->server = new NormalServer($this->config->host, $this->config->user, $this->config->password); + $this->share = $this->server->getShare($this->config->share); + if ($this->config->root) { + $this->root = '/' . $this->config->root . '/' . uniqid(); + } else { + $this->root = '/' . uniqid(); + } + $this->share->mkdir($this->root); + } +} diff --git a/apps/files_external/3rdparty/icewind/smb/tests/bootstrap.php b/apps/files_external/3rdparty/icewind/smb/tests/bootstrap.php new file mode 100644 index 00000000000..dc2e34b183e --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/tests/bootstrap.php @@ -0,0 +1,9 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +date_default_timezone_set('UTC'); +require_once __DIR__.'/../vendor/autoload.php'; diff --git a/apps/files_external/3rdparty/icewind/smb/tests/config.json b/apps/files_external/3rdparty/icewind/smb/tests/config.json new file mode 100644 index 00000000000..0ecd7e3715d --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/tests/config.json @@ -0,0 +1,7 @@ +{ + "host": "localhost", + "user": "test", + "password": "test", + "share": "test", + "root": "test" +} diff --git a/apps/files_external/3rdparty/icewind/smb/tests/phpunit.xml b/apps/files_external/3rdparty/icewind/smb/tests/phpunit.xml new file mode 100644 index 00000000000..3ab244dd34f --- /dev/null +++ b/apps/files_external/3rdparty/icewind/smb/tests/phpunit.xml @@ -0,0 +1,6 @@ + + + + ./ + + diff --git a/apps/files_external/3rdparty/icewind/streams/.gitignore b/apps/files_external/3rdparty/icewind/streams/.gitignore new file mode 100644 index 00000000000..4f389129e2d --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/.gitignore @@ -0,0 +1,3 @@ +.idea +vendor +composer.lock diff --git a/apps/files_external/3rdparty/icewind/streams/.travis.yml b/apps/files_external/3rdparty/icewind/streams/.travis.yml new file mode 100644 index 00000000000..dfa52767dda --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/.travis.yml @@ -0,0 +1,26 @@ +language: php +php: + - 5.3 + - 5.4 + - 5.5 + - hhvm + +matrix: + allow_failures: + - php: hhvm # due to facebook/hhvm#3321 + +env: + global: + - CURRENT_DIR=`pwd` + +install: + - composer install --dev --no-interaction + +script: + - mkdir -p build/logs + - cd tests + - phpunit --coverage-clover ../build/logs/clover.xml --configuration phpunit.xml + +after_script: + - cd $CURRENT_DIR + - php vendor/bin/coveralls -v diff --git a/apps/files_external/3rdparty/icewind/streams/README.md b/apps/files_external/3rdparty/icewind/streams/README.md new file mode 100644 index 00000000000..54f6d19a560 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/README.md @@ -0,0 +1,52 @@ +#Streams# + +[![Build Status](https://travis-ci.org/icewind1991/Streams.svg?branch=master)](https://travis-ci.org/icewind1991/Streams) +[![Coverage Status](https://img.shields.io/coveralls/icewind1991/Streams.svg)](https://coveralls.io/r/icewind1991/Streams?branch=master) + +Generic stream wrappers for php. + +##CallBackWrapper## + +A `CallBackWrapper` can be used to register callbacks on read, write and closing of the stream, +it wraps an existing stream and can thus be used for any stream in php + +The callbacks are passed in the stream context along with the source stream +and can be any valid [php callable](http://php.net/manual/en/language.types.callable.php) + +###Example### +```php +=5.3" + }, + "require-dev" : { + "satooshi/php-coveralls": "dev-master" + }, + "autoload" : { + "psr-4": { + "Icewind\\Streams\\Tests\\": "tests/", + "Icewind\\Streams\\": "src/" + } + } +} diff --git a/apps/files_external/3rdparty/icewind/streams/src/CallbackWrapper.php b/apps/files_external/3rdparty/icewind/streams/src/CallbackWrapper.php new file mode 100644 index 00000000000..fd99aa6ebe8 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/src/CallbackWrapper.php @@ -0,0 +1,110 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\Streams; + +/** + * Wrapper that provides callbacks for write, read and close + * + * The following options should be passed in the context when opening the stream + * [ + * 'callback' => [ + * 'source' => resource + * 'read' => function($count){} (optional) + * 'write' => function($data){} (optional) + * 'close' => function(){} (optional) + * ] + * ] + * + * All callbacks are called after the operation is executed on the source stream + */ +class CallbackWrapper extends Wrapper { + /** + * @var callable + */ + protected $readCallback; + + /** + * @var callable + */ + protected $writeCallback; + + /** + * @var callable + */ + protected $closeCallback; + + /** + * Wraps a stream with the provided callbacks + * + * @param resource $source + * @param callable $read (optional) + * @param callable $write (optional) + * @param callable $close (optional) + * @return resource + * + * @throws \BadMethodCallException + */ + public static function wrap($source, $read = null, $write = null, $close = null) { + $context = stream_context_create(array( + 'callback' => array( + 'source' => $source, + 'read' => $read, + 'write' => $write, + 'close' => $close + ) + )); + stream_wrapper_register('callback', '\Icewind\Streams\CallbackWrapper'); + try { + $wrapped = fopen('callback://', 'r+', false, $context); + } catch (\BadMethodCallException $e) { + stream_wrapper_unregister('callback'); + throw $e; + } + stream_wrapper_unregister('callback'); + return $wrapped; + } + + public function stream_open($path, $mode, $options, &$opened_path) { + $context = $this->loadContext('callback'); + + if (isset($context['read']) and is_callable($context['read'])) { + $this->readCallback = $context['read']; + } + if (isset($context['write']) and is_callable($context['write'])) { + $this->writeCallback = $context['write']; + } + if (isset($context['close']) and is_callable($context['close'])) { + $this->closeCallback = $context['close']; + } + return true; + } + + public function stream_read($count) { + $result = parent::stream_read($count); + if ($this->readCallback) { + call_user_func($this->readCallback, $count); + } + return $result; + } + + public function stream_write($data) { + $result = parent::stream_write($data); + if ($this->writeCallback) { + call_user_func($this->writeCallback, $data); + } + return $result; + } + + public function stream_close() { + $result = parent::stream_close(); + if ($this->closeCallback) { + call_user_func($this->closeCallback); + } + return $result; + } +} diff --git a/apps/files_external/3rdparty/icewind/streams/src/Directory.php b/apps/files_external/3rdparty/icewind/streams/src/Directory.php new file mode 100644 index 00000000000..c80a878386b --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/src/Directory.php @@ -0,0 +1,35 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\Streams; + +/** + * Interface for stream wrappers that implements a directory + */ +interface Directory { + /** + * @param string $path + * @param array $options + * @return bool + */ + public function dir_opendir($path, $options); + + /** + * @return string + */ + public function dir_readdir(); + + /** + * @return bool + */ + public function dir_closedir(); + + /** + * @return bool + */ + public function dir_rewinddir(); +} diff --git a/apps/files_external/3rdparty/icewind/streams/src/File.php b/apps/files_external/3rdparty/icewind/streams/src/File.php new file mode 100644 index 00000000000..6202ef4a4b4 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/src/File.php @@ -0,0 +1,86 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\Streams; + +/** + * Interface for stream wrappers that implements a file + */ +interface File { + /** + * @param string $path + * @param string $mode + * @param int $options + * @param string &$opened_path + * @return bool + */ + public function stream_open($path, $mode, $options, &$opened_path); + + /** + * @param string $offset + * @param int $whence + * @return bool + */ + public function stream_seek($offset, $whence = SEEK_SET); + + /** + * @return int + */ + public function stream_tell(); + + /** + * @param int $count + * @return string + */ + public function stream_read($count); + + /** + * @param string $data + * @return int + */ + public function stream_write($data); + + /** + * @param int $option + * @param int $arg1 + * @param int $arg2 + * @return bool + */ + public function stream_set_option($option, $arg1, $arg2); + + /** + * @param int $size + * @return bool + */ + public function stream_truncate($size); + + /** + * @return array + */ + public function stream_stat(); + + /** + * @param int $operation + * @return bool + */ + public function stream_lock($operation); + + /** + * @return bool + */ + public function stream_flush(); + + /** + * @return bool + */ + public function stream_eof(); + + /** + * @return bool + */ + public function stream_close(); +} diff --git a/apps/files_external/3rdparty/icewind/streams/src/IteratorDirectory.php b/apps/files_external/3rdparty/icewind/streams/src/IteratorDirectory.php new file mode 100644 index 00000000000..c4eac5d4ed3 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/src/IteratorDirectory.php @@ -0,0 +1,123 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\Streams; + +/** + * Create a directory handle from an iterator or array + * + * The following options should be passed in the context when opening the stream + * [ + * 'dir' => [ + * 'array' => string[] + * 'iterator' => \Iterator + * ] + * ] + * + * Either 'array' or 'iterator' need to be set, if both are set, 'iterator' takes preference + */ +class IteratorDirectory implements Directory { + /** + * @var resource + */ + public $context; + + /** + * @var \Iterator + */ + protected $iterator; + + /** + * Load the source from the stream context and return the context options + * + * @param string $name + * @return array + * @throws \Exception + */ + protected function loadContext($name) { + $context = stream_context_get_options($this->context); + if (isset($context[$name])) { + $context = $context[$name]; + } else { + throw new \BadMethodCallException('Invalid context, "' . $name . '" options not set'); + } + if (isset($context['iterator']) and $context['iterator'] instanceof \Iterator) { + $this->iterator = $context['iterator']; + } else if (isset($context['array']) and is_array($context['array'])) { + $this->iterator = new \ArrayIterator($context['array']); + } else { + throw new \BadMethodCallException('Invalid context, iterator or array not set'); + } + return $context; + } + + /** + * @param string $path + * @param array $options + * @return bool + */ + public function dir_opendir($path, $options) { + $this->loadContext('dir'); + return true; + } + + /** + * @return string + */ + public function dir_readdir() { + if ($this->iterator->valid()) { + $result = $this->iterator->current(); + $this->iterator->next(); + return $result; + } else { + return false; + } + } + + /** + * @return bool + */ + public function dir_closedir() { + return true; + } + + /** + * @return bool + */ + public function dir_rewinddir() { + $this->iterator->rewind(); + return true; + } + + /** + * Creates a directory handle from the provided array or iterator + * + * @param \Iterator | array $source + * @return resource + * + * @throws \BadMethodCallException + */ + public static function wrap($source) { + if ($source instanceof \Iterator) { + $context = stream_context_create(array( + 'dir' => array( + 'iterator' => $source) + )); + } else if (is_array($source)) { + $context = stream_context_create(array( + 'dir' => array( + 'array' => $source) + )); + } else { + throw new \BadMethodCallException('$source should be an Iterator or array'); + } + stream_wrapper_register('iterator', '\Icewind\Streams\IteratorDirectory'); + $wrapped = opendir('iterator://', $context); + stream_wrapper_unregister('iterator'); + return $wrapped; + } +} diff --git a/apps/files_external/3rdparty/icewind/streams/src/NullWrapper.php b/apps/files_external/3rdparty/icewind/streams/src/NullWrapper.php new file mode 100644 index 00000000000..8cbaaa756d3 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/src/NullWrapper.php @@ -0,0 +1,42 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\Streams; + +/** + * Stream wrapper that does nothing, used for tests + */ +class NullWrapper extends Wrapper { + /** + * Wraps a stream with the provided callbacks + * + * @param resource $source + * @return resource + * + * @throws \BadMethodCallException + */ + public static function wrap($source) { + $context = stream_context_create(array( + 'null' => array( + 'source' => $source) + )); + stream_wrapper_register('null', '\Icewind\Streams\NullWrapper'); + try { + $wrapped = fopen('null://', 'r+', false, $context); + } catch (\BadMethodCallException $e) { + stream_wrapper_unregister('null'); + throw $e; + } + stream_wrapper_unregister('null'); + return $wrapped; + } + + public function stream_open($path, $mode, $options, &$opened_path) { + $this->loadContext('null'); + return true; + } +} diff --git a/apps/files_external/3rdparty/icewind/streams/src/Wrapper.php b/apps/files_external/3rdparty/icewind/streams/src/Wrapper.php new file mode 100644 index 00000000000..2e3a6e6cd88 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/src/Wrapper.php @@ -0,0 +1,110 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\Streams; + +/** + * Base class for stream wrappers, wraps an existing stream + * + * This wrapper itself doesn't implement any functionality but is just a base class for other wrappers to extend + */ +abstract class Wrapper implements File { + /** + * @var resource + */ + public $context; + + /** + * The wrapped stream + * + * @var resource + */ + protected $source; + + /** + * Load the source from the stream context and return the context options + * + * @param string $name + * @return array + * @throws \Exception + */ + protected function loadContext($name) { + $context = stream_context_get_options($this->context); + if (isset($context[$name])) { + $context = $context[$name]; + } else { + throw new \BadMethodCallException('Invalid context, "callable" options not set'); + } + if (isset($context['source']) and is_resource($context['source'])) { + $this->setSourceStream($context['source']); + } else { + throw new \BadMethodCallException('Invalid context, source not set'); + } + return $context; + } + + /** + * @param resource $source + */ + protected function setSourceStream($source) { + $this->source = $source; + } + + public function stream_seek($offset, $whence = SEEK_SET) { + $result = fseek($this->source, $offset, $whence); + return $result == 0 ? true : false; + } + + public function stream_tell() { + return ftell($this->source); + } + + public function stream_read($count) { + return fread($this->source, $count); + } + + public function stream_write($data) { + return fwrite($this->source, $data); + } + + public function stream_set_option($option, $arg1, $arg2) { + switch ($option) { + case STREAM_OPTION_BLOCKING: + stream_set_blocking($this->source, $arg1); + break; + case STREAM_OPTION_READ_TIMEOUT: + stream_set_timeout($this->source, $arg1, $arg2); + break; + case STREAM_OPTION_WRITE_BUFFER: + stream_set_write_buffer($this->source, $arg1); + } + } + + public function stream_truncate($size) { + return ftruncate($this->source, $size); + } + + public function stream_stat() { + return fstat($this->source); + } + + public function stream_lock($mode) { + return flock($this->source, $mode); + } + + public function stream_flush() { + return fflush($this->source); + } + + public function stream_eof() { + return feof($this->source); + } + + public function stream_close() { + return fclose($this->source); + } +} diff --git a/apps/files_external/3rdparty/icewind/streams/tests/CallbackWrapper.php b/apps/files_external/3rdparty/icewind/streams/tests/CallbackWrapper.php new file mode 100644 index 00000000000..229b629dcd9 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/tests/CallbackWrapper.php @@ -0,0 +1,72 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\Streams\Tests; + +class CallbackWrapper extends Wrapper { + + /** + * @param resource $source + * @param callable $read + * @param callable $write + * @param callable $close + * @return resource + */ + protected function wrapSource($source, $read = null, $write = null, $close = null) { + return \Icewind\Streams\CallbackWrapper::wrap($source, $read, $write, $close); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testWrapInvalidSource() { + $this->wrapSource('foo'); + } + + public function testReadCallback() { + $called = false; + $callBack = function () use (&$called) { + $called = true; + }; + + $source = fopen('php://temp', 'r+'); + fwrite($source, 'foobar'); + rewind($source); + + $wrapped = $this->wrapSource($source, $callBack); + $this->assertEquals('foo', fread($wrapped, 3)); + $this->assertTrue($called); + } + + public function testWriteCallback() { + $lastData = ''; + $callBack = function ($data) use (&$lastData) { + $lastData = $data; + }; + + $source = fopen('php://temp', 'r+'); + + $wrapped = $this->wrapSource($source, null, $callBack); + fwrite($wrapped, 'foobar'); + $this->assertEquals('foobar', $lastData); + } + + public function testCloseCallback() { + $called = false; + $callBack = function () use (&$called) { + $called = true; + }; + + $source = fopen('php://temp', 'r+'); + fwrite($source, 'foobar'); + rewind($source); + + $wrapped = $this->wrapSource($source, null, null, $callBack); + fclose($wrapped); + $this->assertTrue($called); + } +} diff --git a/apps/files_external/3rdparty/icewind/streams/tests/IteratorDirectory.php b/apps/files_external/3rdparty/icewind/streams/tests/IteratorDirectory.php new file mode 100644 index 00000000000..0d990468368 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/tests/IteratorDirectory.php @@ -0,0 +1,130 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\Streams\Tests; + +class IteratorDirectory extends \PHPUnit_Framework_TestCase { + + /** + * @param \Iterator | array $source + * @return resource + */ + protected function wrapSource($source) { + return \Icewind\Streams\IteratorDirectory::wrap($source); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testNoContext() { + $context = stream_context_create(array()); + stream_wrapper_register('iterator', '\Icewind\Streams\IteratorDirectory'); + try { + opendir('iterator://', $context); + stream_wrapper_unregister('iterator'); + } catch (\Exception $e) { + stream_wrapper_unregister('iterator'); + throw $e; + } + } + + /** + * @expectedException \BadMethodCallException + */ + public function testInvalidSource() { + $context = stream_context_create(array( + 'dir' => array( + 'array' => 2 + ) + )); + stream_wrapper_register('iterator', '\Icewind\Streams\IteratorDirectory'); + try { + opendir('iterator://', $context); + stream_wrapper_unregister('iterator'); + } catch (\Exception $e) { + stream_wrapper_unregister('iterator'); + throw $e; + } + } + + /** + * @expectedException \BadMethodCallException + */ + public function testWrapInvalidSource() { + $this->wrapSource(2); + } + + public function fileListProvider() { + $longList = array_fill(0, 500, 'foo'); + return array( + array( + array( + 'foo', + 'bar', + 'qwerty' + ) + ), + array( + array( + 'with spaces', + 'under_scores', + '日本語', + 'character %$_', + '.', + '0', + 'double "quotes"', + "single 'quotes'" + ) + ), + array( + array( + 'single item' + ) + ), + array( + $longList + ), + array( + array() + ) + ); + } + + protected function basicTest($fileList, $dh) { + $result = array(); + + while (($file = readdir($dh)) !== false) { + $result[] = $file; + } + + $this->assertEquals($fileList, $result); + + rewinddir($dh); + if (count($fileList)) { + $this->assertEquals($fileList[0], readdir($dh)); + } else { + $this->assertFalse(readdir($dh)); + } + } + + /** + * @dataProvider fileListProvider + */ + public function testBasicIterator($fileList) { + $iterator = new \ArrayIterator($fileList); + $dh = $this->wrapSource($iterator); + $this->basicTest($fileList, $dh); + } + + /** + * @dataProvider fileListProvider + */ + public function testBasicArray($fileList) { + $dh = $this->wrapSource($fileList); + $this->basicTest($fileList, $dh); + } +} diff --git a/apps/files_external/3rdparty/icewind/streams/tests/NullWrapper.php b/apps/files_external/3rdparty/icewind/streams/tests/NullWrapper.php new file mode 100644 index 00000000000..ba42b4dfea1 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/tests/NullWrapper.php @@ -0,0 +1,59 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\Streams\Tests; + +class NullWrapper extends Wrapper { + + /** + * @param resource $source + * @return resource + */ + protected function wrapSource($source) { + return \Icewind\Streams\NullWrapper::wrap($source); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testNoContext() { + stream_wrapper_register('null', '\Icewind\Streams\NullWrapper'); + $context = stream_context_create(array()); + try { + fopen('null://', 'r+', false, $context); + stream_wrapper_unregister('null'); + } catch (\Exception $e) { + stream_wrapper_unregister('null'); + throw $e; + } + } + + /** + * @expectedException \BadMethodCallException + */ + public function testNoSource() { + stream_wrapper_register('null', '\Icewind\Streams\NullWrapper'); + $context = stream_context_create(array( + 'null' => array( + 'source' => 'bar' + ) + )); + try { + fopen('null://', 'r+', false, $context); + } catch (\Exception $e) { + stream_wrapper_unregister('null'); + throw $e; + } + } + + /** + * @expectedException \BadMethodCallException + */ + public function testWrapInvalidSource() { + $this->wrapSource('foo'); + } +} diff --git a/apps/files_external/3rdparty/icewind/streams/tests/Wrapper.php b/apps/files_external/3rdparty/icewind/streams/tests/Wrapper.php new file mode 100644 index 00000000000..6bb644dd611 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/tests/Wrapper.php @@ -0,0 +1,105 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\Streams\Tests; + +abstract class Wrapper extends \PHPUnit_Framework_TestCase { + /** + * @param resource $source + * @return resource + */ + abstract protected function wrapSource($source); + + public function testRead() { + $source = fopen('php://temp', 'r+'); + fwrite($source, 'foobar'); + rewind($source); + + $wrapped = $this->wrapSource($source); + $this->assertEquals('foo', fread($wrapped, 3)); + $this->assertEquals('bar', fread($wrapped, 3)); + $this->assertEquals('', fread($wrapped, 3)); + } + + public function testWrite() { + $source = fopen('php://temp', 'r+'); + rewind($source); + + $wrapped = $this->wrapSource($source); + + $this->assertEquals(6, fwrite($wrapped, 'foobar')); + rewind($source); + $this->assertEquals('foobar', stream_get_contents($source)); + } + + public function testClose() { + $source = fopen('php://temp', 'r+'); + rewind($source); + + $wrapped = $this->wrapSource($source); + + fclose($wrapped); + $this->assertFalse(is_resource($source)); + } + + public function testSeekTell() { + $source = fopen('php://temp', 'r+'); + fwrite($source, 'foobar'); + rewind($source); + + $wrapped = $this->wrapSource($source); + + $this->assertEquals(0, ftell($wrapped)); + + fseek($wrapped, 2); + $this->assertEquals(2, ftell($source)); + $this->assertEquals(2, ftell($wrapped)); + + fseek($wrapped, 2, SEEK_CUR); + $this->assertEquals(4, ftell($source)); + $this->assertEquals(4, ftell($wrapped)); + + fseek($wrapped, -1, SEEK_END); + $this->assertEquals(5, ftell($source)); + $this->assertEquals(5, ftell($wrapped)); + } + + public function testStat() { + $source = fopen(__FILE__, 'r+'); + $wrapped = $this->wrapSource($source); + $this->assertEquals(stat(__FILE__), fstat($wrapped)); + } + + public function testTruncate() { + if (version_compare(phpversion(), '5.4.0', '<')) { + $this->markTestSkipped('php <5.4 doesn\'t support truncate for stream wrappers'); + } + $source = fopen('php://temp', 'r+'); + fwrite($source, 'foobar'); + rewind($source); + $wrapped = $this->wrapSource($source); + + ftruncate($wrapped, 2); + $this->assertEquals('fo', fread($wrapped, 10)); + } + + public function testLock() { + $source = tmpfile(); + $wrapped = $this->wrapSource($source); + if (!flock($wrapped, LOCK_EX)) { + $this->fail('Unable to acquire lock'); + } + } + + public function testStreamOptions() { + $source = fopen('php://temp', 'r+'); + $wrapped = $this->wrapSource($source); + stream_set_blocking($wrapped, 0); + stream_set_timeout($wrapped, 1, 0); + stream_set_write_buffer($wrapped, 0); + } +} diff --git a/apps/files_external/3rdparty/icewind/streams/tests/bootstrap.php b/apps/files_external/3rdparty/icewind/streams/tests/bootstrap.php new file mode 100644 index 00000000000..2c17fd57feb --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/tests/bootstrap.php @@ -0,0 +1,9 @@ + + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +date_default_timezone_set('UTC'); +require_once __DIR__ . '/../vendor/autoload.php'; diff --git a/apps/files_external/3rdparty/icewind/streams/tests/phpunit.xml b/apps/files_external/3rdparty/icewind/streams/tests/phpunit.xml new file mode 100644 index 00000000000..e3d96352c43 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/tests/phpunit.xml @@ -0,0 +1,6 @@ + + + + ./ + + diff --git a/apps/files_external/3rdparty/smb4php/smb.php b/apps/files_external/3rdparty/smb4php/smb.php deleted file mode 100644 index e325506fa14..00000000000 --- a/apps/files_external/3rdparty/smb4php/smb.php +++ /dev/null @@ -1,516 +0,0 @@ - -# Copyright (c) 2012 Frank Karlitschek -# Copyright (c) 2014 Robin McCorkell -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program 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 General Public License for more details. -# -# On the official website http://www.phpclasses.org/smb4php the -# license is listed as LGPL so we assume that this is -# dual-licensed GPL/LGPL -################################################################### - -define ('SMB4PHP_VERSION', '0.8'); - -################################################################### -# CONFIGURATION SECTION - Change for your needs -################################################################### - -define ('SMB4PHP_SMBCLIENT', 'smbclient'); -define ('SMB4PHP_SMBOPTIONS', 'TCP_NODELAY IPTOS_LOWDELAY SO_KEEPALIVE SO_RCVBUF=8192 SO_SNDBUF=8192'); -define ('SMB4PHP_AUTHMODE', 'arg'); # set to 'env' to use USER enviroment variable - -################################################################### -# SMB - commands that does not need an instance -################################################################### - -$GLOBALS['__smb_cache'] = array ('stat' => array (), 'dir' => array ()); - -class smb { - - private static $regexp = array ( - '^added interface ip=(.*) bcast=(.*) nmask=(.*)$' => 'skip', - 'Anonymous login successful' => 'skip', - '^Domain=\[(.*)\] OS=\[(.*)\] Server=\[(.*)\]$' => 'skip', - '^\tSharename[ ]+Type[ ]+Comment$' => 'shares', - '^\t---------[ ]+----[ ]+-------$' => 'skip', - '^\tServer [ ]+Comment$' => 'servers', - '^\t---------[ ]+-------$' => 'skip', - '^\tWorkgroup[ ]+Master$' => 'workg', - '^\t(.*)[ ]+(Disk|IPC)[ ]+IPC.*$' => 'skip', - '^\tIPC\\\$(.*)[ ]+IPC' => 'skip', - '^\t(.*)[ ]+(Disk)[ ]+(.*)$' => 'share', - '^\t(.*)[ ]+(Printer)[ ]+(.*)$' => 'skip', - '([0-9]+) blocks of size ([0-9]+)\. ([0-9]+) blocks available' => 'skip', - 'Got a positive name query response from ' => 'skip', - '^(session setup failed): (.*)$' => 'error', - '^(.*): ERRSRV - ERRbadpw' => 'error', - '^Error returning browse list: (.*)$' => 'error', - '^tree connect failed: (.*)$' => 'error', - '^(Connection to .* failed)(.*)$' => 'error-connect', - '^NT_STATUS_(.*) ' => 'error', - '^NT_STATUS_(.*)\$' => 'error', - 'ERRDOS - ERRbadpath \((.*).\)' => 'error', - 'cd (.*): (.*)$' => 'error', - '^cd (.*): NT_STATUS_(.*)' => 'error', - '^\t(.*)$' => 'srvorwg', - '^([0-9]+)[ ]+([0-9]+)[ ]+(.*)$' => 'skip', - '^Job ([0-9]+) cancelled' => 'skip', - '^[ ]+(.*)[ ]+([0-9]+)[ ]+(Mon|Tue|Wed|Thu|Fri|Sat|Sun)[ ](Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[ ]+([0-9]+)[ ]+([0-9]{2}:[0-9]{2}:[0-9]{2})[ ]([0-9]{4})$' => 'files', - '^message start: ERRSRV - (ERRmsgoff)' => 'error' - ); - - function getRegexp() { - return self::$regexp; - } - - function parse_url ($url) { - $pu = parse_url (trim($url)); - foreach (array ('domain', 'user', 'pass', 'host', 'port', 'path') as $i) { - if (! isset($pu[$i])) { - $pu[$i] = ''; - } - } - if (count ($userdomain = explode (';', urldecode ($pu['user']))) > 1) { - @list ($pu['domain'], $pu['user']) = $userdomain; - } - $path = preg_replace (array ('/^\//', '/\/$/'), '', urldecode ($pu['path'])); - list ($pu['share'], $pu['path']) = (preg_match ('/^([^\/]+)\/(.*)/', $path, $regs)) - ? array ($regs[1], preg_replace ('/\//', '\\', $regs[2])) - : array ($path, ''); - $pu['type'] = $pu['path'] ? 'path' : ($pu['share'] ? 'share' : ($pu['host'] ? 'host' : '**error**')); - if (! ($pu['port'] = intval(@$pu['port']))) { - $pu['port'] = 139; - } - - // decode user and password - $pu['user'] = urldecode($pu['user']); - $pu['pass'] = urldecode($pu['pass']); - return $pu; - } - - - function look ($purl) { - return smb::client ('-L ' . escapeshellarg ($purl['host']), $purl); - } - - - function execute ($command, $purl, $regexp = NULL) { - return smb::client ('-d 0 ' - . escapeshellarg ('//' . $purl['host'] . '/' . $purl['share']) - . ' -c ' . escapeshellarg ($command), $purl, $regexp - ); - } - - function client ($params, $purl, $regexp = NULL) { - - if ($regexp === NULL) $regexp = smb::$regexp; - - if (SMB4PHP_AUTHMODE == 'env') { - putenv("USER={$purl['user']}%{$purl['pass']}"); - $auth = ''; - } else { - $auth = ($purl['user'] <> '' ? (' -U ' . escapeshellarg ($purl['user'] . '%' . $purl['pass'])) : ''); - } - if ($purl['domain'] <> '') { - $auth .= ' -W ' . escapeshellarg ($purl['domain']); - } - $port = ($purl['port'] <> 139 ? ' -p ' . escapeshellarg ($purl['port']) : ''); - $options = '-O ' . escapeshellarg(SMB4PHP_SMBOPTIONS); - - // this put env is necessary to read the output of smbclient correctly - $old_locale = getenv('LC_ALL'); - putenv('LC_ALL=en_US.UTF-8'); - $output = popen ('TZ=UTC '.SMB4PHP_SMBCLIENT." -N {$auth} {$options} {$port} {$options} {$params} 2>/dev/null", 'r'); - $gotInfo = false; - $info = array (); - $info['info']= array (); - $mode = ''; - while ($line = fgets ($output, 4096)) { - list ($tag, $regs, $i) = array ('skip', array (), array ()); - reset ($regexp); - foreach ($regexp as $r => $t) if (preg_match ('/'.$r.'/', $line, $regs)) { - $tag = $t; - break; - } - switch ($tag) { - case 'skip': continue; - case 'shares': $mode = 'shares'; break; - case 'servers': $mode = 'servers'; break; - case 'workg': $mode = 'workgroups'; break; - case 'share': - list($name, $type) = array ( - trim(substr($line, 1, 15)), - trim(strtolower(substr($line, 17, 10))) - ); - $i = ($type <> 'disk' && preg_match('/^(.*) Disk/', $line, $regs)) - ? array(trim($regs[1]), 'disk') - : array($name, 'disk'); - break; - case 'srvorwg': - list ($name, $master) = array ( - strtolower(trim(substr($line,1,21))), - strtolower(trim(substr($line, 22))) - ); - $i = ($mode == 'servers') ? array ($name, "server") : array ($name, "workgroup", $master); - break; - case 'files': - list ($attr, $name) = preg_match ("/^(.*)[ ]+([D|A|H|N|S|R]+)$/", trim ($regs[1]), $regs2) - ? array (trim ($regs2[2]), trim ($regs2[1])) - : array ('', trim ($regs[1])); - list ($his, $im) = array ( - explode(':', $regs[6]), 1 + strpos("JanFebMarAprMayJunJulAugSepOctNovDec", $regs[4]) / 3); - $i = ($name <> '.' && $name <> '..') - ? array ( - $name, - (strpos($attr,'D') === FALSE) ? 'file' : 'folder', - 'attr' => $attr, - 'size' => intval($regs[2]), - 'time' => mktime ($his[0], $his[1], $his[2], $im, $regs[5], $regs[7]) - ) - : array(); - break; - case 'error': - if(substr($regs[0],0,22)=='NT_STATUS_NO_SUCH_FILE'){ - return false; - }elseif(substr($regs[0],0,31)=='NT_STATUS_OBJECT_NAME_COLLISION'){ - return false; - }elseif(substr($regs[0],0,31)=='NT_STATUS_OBJECT_PATH_NOT_FOUND'){ - return false; - }elseif(substr($regs[0],0,31)=='NT_STATUS_OBJECT_NAME_NOT_FOUND'){ - return false; - }elseif(substr($regs[0],0,29)=='NT_STATUS_FILE_IS_A_DIRECTORY'){ - return false; - } - trigger_error($regs[0].' params('.$params.')', E_USER_ERROR); - case 'error-connect': - // connection error can happen after obtaining share list if - // NetBIOS is disabled/blocked on the target server, - // in which case we keep the info and continue - if (!$gotInfo) { - return false; - } - } - if ($i) switch ($i[1]) { - case 'file': - case 'folder': $info['info'][$i[0]] = $i; - case 'disk': - case 'server': - case 'workgroup': $info[$i[1]][] = $i[0]; - $gotInfo = true; - } - } - pclose($output); - - - // restore previous locale - if ($old_locale===false) { - putenv('LC_ALL'); - } else { - putenv('LC_ALL='.$old_locale); - } - - return $info; - } - - - # stats - - function url_stat ($url, $flags = STREAM_URL_STAT_LINK) { - if ($s = smb::getstatcache($url)) { - return $s; - } - list ($stat, $pu) = array (false, smb::parse_url ($url)); - switch ($pu['type']) { - case 'host': - if ($o = smb::look ($pu)) - $stat = stat ("/tmp"); - else - trigger_error ("url_stat(): list failed for host '{$pu['host']}'", E_USER_WARNING); - break; - case 'share': - if (smb::execute("ls", $pu)) - $stat = stat ("/tmp"); - else - trigger_error ("url_stat(): disk resource '{$pu['share']}' not found in '{$pu['host']}'", E_USER_WARNING); - break; - case 'path': - if ($o = smb::execute ('dir "'.$pu['path'].'"', $pu)) { - $p = explode('\\', $pu['path']); - $name = $p[count($p)-1]; - if (isset ($o['info'][$name])) { - $stat = smb::addstatcache ($url, $o['info'][$name]); - } else { - trigger_error ("url_stat(): path '{$pu['path']}' not found", E_USER_WARNING); - } - } else { - return false; -// trigger_error ("url_stat(): dir failed for path '{$pu['path']}'", E_USER_WARNING); - } - break; - default: trigger_error ('error in URL', E_USER_ERROR); - } - return $stat; - } - - function addstatcache ($url, $info) { - $url = str_replace('//', '/', $url); - $url = rtrim($url, '/'); - global $__smb_cache; - $is_file = (strpos ($info['attr'],'D') === FALSE); - $s = ($is_file) ? stat ('/etc/passwd') : stat ('/tmp'); - $s[7] = $s['size'] = $info['size']; - $s[8] = $s[9] = $s[10] = $s['atime'] = $s['mtime'] = $s['ctime'] = $info['time']; - return $__smb_cache['stat'][$url] = $s; - } - - function getstatcache ($url) { - $url = str_replace('//', '/', $url); - $url = rtrim($url, '/'); - global $__smb_cache; - return isset ($__smb_cache['stat'][$url]) ? $__smb_cache['stat'][$url] : FALSE; - } - - function clearstatcache ($url='') { - $url = str_replace('//', '/', $url); - $url = rtrim($url, '/'); - global $__smb_cache; - if ($url == '') $__smb_cache['stat'] = array (); else unset ($__smb_cache['stat'][$url]); - } - - - # commands - - function unlink ($url) { - $pu = smb::parse_url($url); - if ($pu['type'] <> 'path') trigger_error('unlink(): error in URL', E_USER_ERROR); - smb::clearstatcache ($url); - smb_stream_wrapper::cleardircache (dirname($url)); - return smb::execute ('del "'.$pu['path'].'"', $pu); - } - - function rename ($url_from, $url_to) { - $replace = false; - list ($from, $to) = array (smb::parse_url($url_from), smb::parse_url($url_to)); - if ($from['host'] <> $to['host'] || - $from['share'] <> $to['share'] || - $from['user'] <> $to['user'] || - $from['pass'] <> $to['pass'] || - $from['domain'] <> $to['domain']) { - trigger_error('rename(): FROM & TO must be in same server-share-user-pass-domain', E_USER_ERROR); - } - if ($from['type'] <> 'path' || $to['type'] <> 'path') { - trigger_error('rename(): error in URL', E_USER_ERROR); - } - smb::clearstatcache ($url_from); - $cmd = ''; - // check if target file exists - if (smb::url_stat($url_to)) { - // delete target file first - $cmd = 'del "' . $to['path'] . '"; '; - $replace = true; - } - $cmd .= 'rename "' . $from['path'] . '" "' . $to['path'] . '"'; - $result = smb::execute($cmd, $to); - if ($replace) { - // clear again, else the cache will return the info - // from the old file - smb::clearstatcache ($url_to); - } - return $result !== false; - } - - function mkdir ($url, $mode, $options) { - $pu = smb::parse_url($url); - if ($pu['type'] <> 'path') trigger_error('mkdir(): error in URL', E_USER_ERROR); - return smb::execute ('mkdir "'.$pu['path'].'"', $pu)!==false; - } - - function rmdir ($url) { - $pu = smb::parse_url($url); - if ($pu['type'] <> 'path') trigger_error('rmdir(): error in URL', E_USER_ERROR); - smb::clearstatcache ($url); - smb_stream_wrapper::cleardircache (dirname($url)); - return smb::execute ('rmdir "'.$pu['path'].'"', $pu)!==false; - } - -} - -################################################################### -# SMB_STREAM_WRAPPER - class to be registered for smb:// URLs -################################################################### - -class smb_stream_wrapper extends smb { - - # variables - - private $stream, $url, $parsed_url = array (), $mode, $tmpfile; - private $need_flush = FALSE; - private $dir = array (), $dir_index = -1; - - - # directories - - function dir_opendir ($url, $options) { - if ($d = $this->getdircache ($url)) { - $this->dir = $d; - $this->dir_index = 0; - return TRUE; - } - $pu = smb::parse_url ($url); - switch ($pu['type']) { - case 'host': - if ($o = smb::look ($pu)) { - $this->dir = $o['disk']; - $this->dir_index = 0; - } else { - trigger_error ("dir_opendir(): list failed for host '{$pu['host']}'", E_USER_WARNING); - return false; - } - break; - case 'share': - case 'path': - if (is_array($o = smb::execute ('dir "'.$pu['path'].'\*"', $pu))) { - $this->dir = array_keys($o['info']); - $this->dir_index = 0; - $this->adddircache ($url, $this->dir); - if(substr($url,-1,1)=='/'){ - $url=substr($url,0,-1); - } - foreach ($o['info'] as $name => $info) { - smb::addstatcache($url . '/' . $name, $info); - } - } else { - trigger_error ("dir_opendir(): dir failed for path '".$pu['path']."'", E_USER_WARNING); - return false; - } - break; - default: - trigger_error ('dir_opendir(): error in URL', E_USER_ERROR); - return false; - } - return TRUE; - } - - function dir_readdir () { - return ($this->dir_index < count($this->dir)) ? $this->dir[$this->dir_index++] : FALSE; - } - - function dir_rewinddir () { $this->dir_index = 0; } - - function dir_closedir () { $this->dir = array(); $this->dir_index = -1; return TRUE; } - - - # cache - - function adddircache ($url, $content) { - $url = str_replace('//', '/', $url); - $url = rtrim($url, '/'); - global $__smb_cache; - return $__smb_cache['dir'][$url] = $content; - } - - function getdircache ($url) { - $url = str_replace('//', '/', $url); - $url = rtrim($url, '/'); - global $__smb_cache; - return isset ($__smb_cache['dir'][$url]) ? $__smb_cache['dir'][$url] : FALSE; - } - - function cleardircache ($url='') { - $url = str_replace('//', '/', $url); - $url = rtrim($url, '/'); - global $__smb_cache; - if ($url == ''){ - $__smb_cache['dir'] = array (); - }else{ - unset ($__smb_cache['dir'][$url]); - } - } - - - # streams - - function stream_open ($url, $mode, $options, $opened_path) { - $this->url = $url; - $this->mode = $mode; - $this->parsed_url = $pu = smb::parse_url($url); - if ($pu['type'] <> 'path') trigger_error('stream_open(): error in URL', E_USER_ERROR); - switch ($mode) { - case 'r': - case 'r+': - case 'rb': - case 'a': - case 'a+': $this->tmpfile = tempnam('/tmp', 'smb.down.'); - $result = smb::execute ('get "'.$pu['path'].'" "'.$this->tmpfile.'"', $pu); - if($result === false){ - return $result; - } - break; - case 'w': - case 'w+': - case 'wb': - case 'x': - case 'x+': $this->cleardircache(); - $this->tmpfile = tempnam('/tmp', 'smb.up.'); - $this->need_flush=true; - } - $this->stream = fopen ($this->tmpfile, $mode); - return TRUE; - } - - function stream_close () { return fclose($this->stream); } - - function stream_read ($count) { return fread($this->stream, $count); } - - function stream_write ($data) { $this->need_flush = TRUE; return fwrite($this->stream, $data); } - - function stream_eof () { return feof($this->stream); } - - function stream_tell () { return ftell($this->stream); } - - // PATCH: the wrapper must return true when fseek succeeded by returning 0. - function stream_seek ($offset, $whence=null) { return fseek($this->stream, $offset, $whence) === 0; } - - function stream_flush () { - if ($this->mode <> 'r' && $this->need_flush) { - smb::clearstatcache ($this->url); - smb::execute ('put "'.$this->tmpfile.'" "'.$this->parsed_url['path'].'"', $this->parsed_url); - $this->need_flush = FALSE; - } - } - - function stream_stat () { return smb::url_stat ($this->url); } - - function __destruct () { - if ($this->tmpfile <> '') { - if ($this->need_flush) $this->stream_flush (); - unlink ($this->tmpfile); - - } - } - -} - -################################################################### -# Register 'smb' protocol ! -################################################################### - -stream_wrapper_register('smb', 'smb_stream_wrapper') - or die ('Failed to register protocol'); diff --git a/apps/files_external/appinfo/app.php b/apps/files_external/appinfo/app.php index 70f6b0159a6..e74ce3594c1 100644 --- a/apps/files_external/appinfo/app.php +++ b/apps/files_external/appinfo/app.php @@ -22,6 +22,8 @@ OC::$CLASSPATH['OC\Files\Storage\SFTP_Key'] = 'files_external/lib/sftp_key.php'; OC::$CLASSPATH['OC_Mount_Config'] = 'files_external/lib/config.php'; OC::$CLASSPATH['OCA\Files\External\Api'] = 'files_external/lib/api.php'; +require_once __DIR__ . '/../3rdparty/autoload.php'; + OCP\App::registerAdmin('files_external', 'settings'); if (OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes') == 'yes') { OCP\App::registerPersonal('files_external', 'personal'); diff --git a/apps/files_external/lib/smb.php b/apps/files_external/lib/smb.php index 3f0b0f45bfb..571a265e983 100644 --- a/apps/files_external/lib/smb.php +++ b/apps/files_external/lib/smb.php @@ -8,139 +8,271 @@ namespace OC\Files\Storage; -require_once __DIR__ . '/../3rdparty/smb4php/smb.php'; +use Icewind\SMB\Exception\Exception; +use Icewind\SMB\Exception\NotFoundException; +use Icewind\SMB\NativeServer; +use Icewind\SMB\Server; +use Icewind\Streams\CallbackWrapper; +use Icewind\Streams\IteratorDirectory; +use OC\Files\Filesystem; -class SMB extends \OC\Files\Storage\StreamWrapper{ - private $password; - private $user; - private $host; - private $root; - private $share; +class SMB extends Common { + /** + * @var \Icewind\SMB\Server + */ + protected $server; + + /** + * @var \Icewind\SMB\Share + */ + protected $share; + + /** + * @var \Icewind\SMB\FileInfo[] + */ + protected $statCache = array(); public function __construct($params) { if (isset($params['host']) && isset($params['user']) && isset($params['password']) && isset($params['share'])) { - $this->host=$params['host']; - $this->user=$params['user']; - $this->password=$params['password']; - $this->share=$params['share']; - $this->root=isset($params['root'])?$params['root']:'/'; - if ( ! $this->root || $this->root[0]!='/') { - $this->root='/'.$this->root; - } - if (substr($this->root, -1, 1)!='/') { - $this->root.='/'; + if (Server::NativeAvailable()) { + $this->server = new NativeServer($params['host'], $params['user'], $params['password']); + } else { + $this->server = new Server($params['host'], $params['user'], $params['password']); } - if ( ! $this->share || $this->share[0]!='/') { - $this->share='/'.$this->share; - } - if (substr($this->share, -1, 1)=='/') { - $this->share = substr($this->share, 0, -1); + $this->share = $this->server->getShare(trim($params['share'], '/')); + + $this->root = isset($params['root']) ? $params['root'] : '/'; + if (!$this->root || $this->root[0] != '/') { + $this->root = '/' . $this->root; } } else { throw new \Exception('Invalid configuration'); } } - public function getId(){ - return 'smb::' . $this->user . '@' . $this->host . '/' . $this->share . '/' . $this->root; + /** + * @return string + */ + public function getId() { + return 'smb::' . $this->server->getUser() . '@' . $this->server->getHost() . '/' . $this->share->getName() . '/' . $this->root; } - public function constructUrl($path) { - if (substr($path, -1)=='/') { - $path = substr($path, 0, -1); - } - if (substr($path, 0, 1)=='/') { - $path = substr($path, 1); + /** + * @param string $path + * @return string + */ + protected function buildPath($path) { + return Filesystem::normalizePath($this->root . '/' . $path); + } + + /** + * @param string $path + * @return \Icewind\SMB\IFileInfo + */ + protected function getFileInfo($path) { + $path = $this->buildPath($path); + if (!isset($this->statCache[$path])) { + $this->statCache[$path] = $this->share->stat($path); } - // remove trailing dots which some versions of samba don't seem to like - $path = rtrim($path, '.'); - $path = urlencode($path); - $user = urlencode($this->user); - $pass = urlencode($this->password); - return 'smb://'.$user.':'.$pass.'@'.$this->host.$this->share.$this->root.$path; + return $this->statCache[$path]; } - public function stat($path) { - if ( ! $path and $this->root=='/') {//mtime doesn't work for shares - $stat=stat($this->constructUrl($path)); - if (empty($stat)) { - return false; - } - $mtime=$this->shareMTime(); - $stat['mtime']=$mtime; - return $stat; - } else { - $stat = stat($this->constructUrl($path)); + /** + * @param string $path + * @return \Icewind\SMB\IFileInfo[] + */ + protected function getFolderContents($path) { + $path = $this->buildPath($path); + $files = $this->share->dir($path); + foreach ($files as $file) { + $this->statCache[$path . '/' . $file->getName()] = $file; + } + return $files; + } - // smb4php can return an empty array if the connection could not be established - if (empty($stat)) { - return false; - } + /** + * @param \Icewind\SMB\IFileInfo $info + * @return array + */ + protected function formatInfo($info) { + return array( + 'size' => $info->getSize(), + 'mtime' => $info->getMTime() + ); + } - return $stat; - } + /** + * @param string $path + * @return array + */ + public function stat($path) { + return $this->formatInfo($this->getFileInfo($path)); } /** - * Unlinks file or directory * @param string $path + * @return bool */ public function unlink($path) { if ($this->is_dir($path)) { - $this->rmdir($path); - } - else { - $url = $this->constructUrl($path); - unlink($url); - clearstatcache(false, $url); + return $this->rmdir($path); + } else { + $path = $this->buildPath($path); + unset($this->statCache[$path]); + $this->share->del($path); + return true; } - // smb4php still returns false even on success so - // check here whether file was really deleted - return !file_exists($path); } /** * check if a file or folder has been updated since $time + * * @param string $path * @param int $time * @return bool */ - public function hasUpdated($path,$time) { - if(!$path and $this->root=='/') { + public function hasUpdated($path, $time) { + if (!$path and $this->root == '/') { // mtime doesn't work for shares, but giving the nature of the backend, // doing a full update is still just fast enough return true; } else { - $actualTime=$this->filemtime($path); - return $actualTime>$time; + $actualTime = $this->filemtime($path); + return $actualTime > $time; } } /** - * get the best guess for the modification time of the share + * @param string $path + * @param string $mode + * @return resource */ - private function shareMTime() { - $dh=$this->opendir(''); - $lastCtime=0; - if(is_resource($dh)) { - while (($file = readdir($dh)) !== false) { - if ($file!='.' and $file!='..') { - $ctime=$this->filemtime($file); - if ($ctime>$lastCtime) { - $lastCtime=$ctime; + public function fopen($path, $mode) { + $fullPath = $this->buildPath($path); + try { + switch ($mode) { + case 'r': + case 'rb': + if (!$this->file_exists($path)) { + return false; + } + return $this->share->read($fullPath); + case 'w': + case 'wb': + return $this->share->write($fullPath); + case 'a': + case 'ab': + case 'r+': + case 'w+': + case 'wb+': + case 'a+': + case 'x': + case 'x+': + case 'c': + case 'c+': + //emulate these + if (strrpos($path, '.') !== false) { + $ext = substr($path, strrpos($path, '.')); + } else { + $ext = ''; } + if ($this->file_exists($path)) { + if (!$this->isUpdatable($path)) { + return false; + } + $tmpFile = $this->getCachedFile($path); + } else { + if (!$this->isCreatable(dirname($path))) { + return false; + } + $tmpFile = \OCP\Files::tmpFile($ext); + } + $source = fopen($tmpFile, $mode); + $share = $this->share; + return CallBackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath, $share) { + $share->put($tmpFile, $fullPath); + unlink($tmpFile); + }); + } + return false; + } catch (NotFoundException $e) { + return false; + } + } + + public function rmdir($path) { + try { + $this->statCache = array(); + $content = $this->share->dir($this->buildPath($path)); + foreach ($content as $file) { + if ($file->isDirectory()) { + $this->rmdir($path . '/' . $file->getName()); + } else { + $this->share->del($file->getPath()); } } + $this->share->rmdir($this->buildPath($path)); + return true; + } catch (NotFoundException $e) { + return false; + } + } + + public function touch($path, $time = null) { + if (!$this->file_exists($path)) { + $fh = $this->share->write($this->buildPath($path)); + fclose($fh); + return true; + } + return false; + } + + public function opendir($path) { + $files = $this->getFolderContents($path); + $names = array_map(function ($info) { + /** @var \Icewind\SMB\IFileInfo $info */ + return $info->getName(); + }, $files); + return IteratorDirectory::wrap($names); + } + + public function filetype($path) { + try { + return $this->getFileInfo($path)->isDirectory() ? 'dir' : 'file'; + } catch (NotFoundException $e) { + return false; + } + } + + public function mkdir($path) { + $path = $this->buildPath($path); + try { + $this->share->mkdir($path); + return true; + } catch (Exception $e) { + return false; + } + } + + public function file_exists($path) { + try { + $this->getFileInfo($path); + return true; + } catch (NotFoundException $e) { + return false; } - return $lastCtime; } /** * check if smbclient is installed */ public static function checkDependencies() { - $smbClientExists = (bool) \OC_Helper::findBinaryPath('smbclient'); - return $smbClientExists ? true : array('smbclient'); + if (function_exists('shell_exec')) { + $output = shell_exec('command -v smbclient 2> /dev/null'); + if (!empty($output)) { + return true; + } + } + return array('smbclient'); } - } diff --git a/apps/files_external/lib/smb_oc.php b/apps/files_external/lib/smb_oc.php index a7c93d97fd1..245d1ed79b3 100644 --- a/apps/files_external/lib/smb_oc.php +++ b/apps/files_external/lib/smb_oc.php @@ -8,9 +8,12 @@ namespace OC\Files\Storage; -require_once __DIR__ . '/../3rdparty/smb4php/smb.php'; -class SMB_OC extends \OC\Files\Storage\SMB { +use Icewind\SMB\Exception\AccessDeniedException; +use Icewind\SMB\Exception\Exception; +use Icewind\SMB\Server; + +class SMB_OC extends SMB { private $username_as_share; /** @@ -19,18 +22,18 @@ class SMB_OC extends \OC\Files\Storage\SMB { */ public function __construct($params) { if (isset($params['host']) && \OC::$server->getSession()->exists('smb-credentials')) { - $host=$params['host']; + $host = $params['host']; $this->username_as_share = ($params['username_as_share'] === 'true'); $params_auth = json_decode(\OC::$server->getCrypto()->decrypt(\OC::$server->getSession()->get('smb-credentials')), true); $user = \OC::$server->getSession()->get('loginname'); $password = $params_auth['password']; - $root=isset($params['root'])?$params['root']:'/'; + $root = isset($params['root']) ? $params['root'] : '/'; $share = ''; if ($this->username_as_share) { - $share = '/'.$user; + $share = '/' . $user; } elseif (isset($params['share'])) { $share = $params['share']; } else { @@ -84,33 +87,15 @@ class SMB_OC extends \OC\Files\Storage\SMB { } return false; } else { - $smb = new \smb(); - $pu = $smb->parse_url($this->constructUrl('')); - - // Attempt to connect anonymously - $pu['user'] = ''; - $pu['pass'] = ''; - - // Share cannot be checked if dynamic - if ($this->username_as_share) { - if ($smb->look($pu)) { - return true; - } else { - return false; - } - } - if (!$pu['share']) { - return false; - } - - // The following error messages are expected due to anonymous login - $regexp = array( - '(NT_STATUS_ACCESS_DENIED)' => 'skip' - ) + $smb->getRegexp(); + $server = new Server($this->server->getHost(), '', ''); - if ($smb->client("-d 0 " . escapeshellarg('//' . $pu['host'] . '/' . $pu['share']) . " -c exit", $pu, $regexp)) { + try { + $server->listShares(); return true; - } else { + } catch (AccessDeniedException $e) { + // expected due to anonymous login + return true; + } catch (Exception $e) { return false; } } diff --git a/apps/files_external/tests/backends/smb.php b/apps/files_external/tests/backends/smb.php index 4b2f4425ebc..b66459c42ad 100644 --- a/apps/files_external/tests/backends/smb.php +++ b/apps/files_external/tests/backends/smb.php @@ -27,7 +27,7 @@ class SMB extends Storage { protected function tearDown() { if ($this->instance) { - \OCP\Files::rmdirr($this->instance->constructUrl('')); + $this->instance->rmdir(''); } parent::tearDown(); -- 2.39.5