Merge pull request #10673 from owncloud/smb-new
New SMB storage backend
This commit is contained in:
commit
fadf0a9443
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
require_once __DIR__ . '/composer' . '/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit98fe9b281934250b3a93f69a5ce843b3::getLoader();
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "files_external/3rdparty",
|
||||
"description": "3rdparty components for files_external",
|
||||
"license": "MIT",
|
||||
"config": {
|
||||
"vendor-dir": "."
|
||||
},
|
||||
"require": {
|
||||
"icewind/smb": "dev-master",
|
||||
"icewind/streams": "0.2"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"hash": "c854ee7f5bdcb3f2c8ee0a8cfe5e193a",
|
||||
"packages": [
|
||||
{
|
||||
"name": "icewind/smb",
|
||||
"version": "dev-master",
|
||||
"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"
|
||||
},
|
||||
"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": []
|
||||
}
|
|
@ -0,0 +1,413 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* 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 <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = $vendorDir;
|
||||
|
||||
return array(
|
||||
);
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = $vendorDir;
|
||||
|
||||
return array(
|
||||
);
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = $vendorDir;
|
||||
|
||||
return array(
|
||||
'Icewind\\Streams\\Tests\\' => 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'),
|
||||
);
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInit98fe9b281934250b3a93f69a5ce843b3
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit98fe9b281934250b3a93f69a5ce843b3', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit98fe9b281934250b3a93f69a5ce843b3', 'loadClassLoader'));
|
||||
|
||||
$map = require __DIR__ . '/autoload_namespaces.php';
|
||||
foreach ($map as $namespace => $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;
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,2 @@
|
|||
.idea
|
||||
vendor
|
|
@ -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
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
|
||||
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.
|
|
@ -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
|
||||
<?php
|
||||
use Icewind\SMB\Server;
|
||||
|
||||
require('vendor/autoload.php');
|
||||
|
||||
$fileToUpload = __FILE__;
|
||||
|
||||
$server = new Server('localhost', 'test', 'test');
|
||||
$share = $server->getShare('test');
|
||||
$share->put($fileToUpload, 'example.txt');
|
||||
```
|
||||
|
||||
### Download a file ###
|
||||
|
||||
```php
|
||||
<?php
|
||||
use Icewind\SMB\Server;
|
||||
|
||||
require('vendor/autoload.php');
|
||||
|
||||
$target = __DIR__ . '/target.txt';
|
||||
|
||||
$server = new Server('localhost', 'test', 'test');
|
||||
$share = $server->getShare('test');
|
||||
$share->get('example.txt', $target);
|
||||
```
|
||||
|
||||
### List shares on the remote server ###
|
||||
|
||||
```php
|
||||
<?php
|
||||
use Icewind\SMB\Server;
|
||||
|
||||
require('vendor/autoload.php');
|
||||
|
||||
$server = new Server('localhost', 'test', 'test');
|
||||
$shares = $server->listShares();
|
||||
|
||||
foreach ($shares as $share) {
|
||||
echo $share->getName() . "\n";
|
||||
}
|
||||
```
|
||||
|
||||
### List the content of a folder ###
|
||||
|
||||
```php
|
||||
<?php
|
||||
use Icewind\SMB\Server;
|
||||
|
||||
require('vendor/autoload.php');
|
||||
|
||||
$server = new Server('localhost', 'test', 'test');
|
||||
$share = $server->getShare('test');
|
||||
$content = $share->dir('test');
|
||||
|
||||
foreach ($content as $info) {
|
||||
echo $name->getName() . "\n";
|
||||
echo "\tsize :" . $info->getSize() . "\n";
|
||||
}
|
||||
```
|
||||
|
||||
### Using read streams
|
||||
|
||||
```php
|
||||
<?php
|
||||
use Icewind\SMB\Server;
|
||||
|
||||
require('vendor/autoload.php');
|
||||
|
||||
$server = new Server('localhost', 'test', 'test');
|
||||
$share = $server->getShare('test');
|
||||
|
||||
$fh = $share->read('test.txt');
|
||||
echo fread($fh, 4086);
|
||||
fclose($fh);
|
||||
```
|
||||
|
||||
### Using write streams
|
||||
|
||||
```php
|
||||
<?php
|
||||
use Icewind\SMB\Server;
|
||||
|
||||
require('vendor/autoload.php');
|
||||
|
||||
$server = new Server('localhost', 'test', 'test');
|
||||
$share = $server->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
|
||||
<?php
|
||||
use Icewind\SMB\Server;
|
||||
use Icewind\SMB\NativeServer;
|
||||
|
||||
require('vendor/autoload.php');
|
||||
|
||||
$fileToUpload = __FILE__;
|
||||
|
||||
if (Server::NativeAvailable()) {
|
||||
$server = new NativeServer('localhost', 'test', 'test');
|
||||
} else {
|
||||
echo 'libsmbclient-php not available, falling back to wrapping smbclient';
|
||||
$server = new Server('localhost', 'test', 'test');
|
||||
}
|
||||
$share = $server->getShare('test');
|
||||
$share->put($fileToUpload, 'example.txt');
|
||||
```
|
|
@ -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/"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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';
|
||||
}
|
10
apps/files_external/3rdparty/icewind/smb/src/Exception/AccessDeniedException.php
vendored
Normal file
10
apps/files_external/3rdparty/icewind/smb/src/Exception/AccessDeniedException.php
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* This file is licensed under the Licensed under the MIT license:
|
||||
* http://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class AccessDeniedException extends ConnectException {}
|
10
apps/files_external/3rdparty/icewind/smb/src/Exception/AlreadyExistsException.php
vendored
Normal file
10
apps/files_external/3rdparty/icewind/smb/src/Exception/AlreadyExistsException.php
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* This file is licensed under the Licensed under the MIT license:
|
||||
* http://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class AlreadyExistsException extends InvalidRequestException {}
|
10
apps/files_external/3rdparty/icewind/smb/src/Exception/AuthenticationException.php
vendored
Normal file
10
apps/files_external/3rdparty/icewind/smb/src/Exception/AuthenticationException.php
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* This file is licensed under the Licensed under the MIT license:
|
||||
* http://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class AuthenticationException extends ConnectException{}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* This file is licensed under the Licensed under the MIT license:
|
||||
* http://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class ConnectException extends Exception {}
|
10
apps/files_external/3rdparty/icewind/smb/src/Exception/ConnectionException.php
vendored
Normal file
10
apps/files_external/3rdparty/icewind/smb/src/Exception/ConnectionException.php
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* This file is licensed under the Licensed under the MIT license:
|
||||
* http://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class ConnectionException extends ConnectException {}
|
11
apps/files_external/3rdparty/icewind/smb/src/Exception/ConnectionRefusedException.php
vendored
Normal file
11
apps/files_external/3rdparty/icewind/smb/src/Exception/ConnectionRefusedException.php
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* This file is licensed under the Licensed under the MIT license:
|
||||
* http://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class ConnectionRefusedException extends ConnectException {
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* This file is licensed under the Licensed under the MIT license:
|
||||
* http://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class Exception extends \Exception {}
|
10
apps/files_external/3rdparty/icewind/smb/src/Exception/FileInUseException.php
vendored
Normal file
10
apps/files_external/3rdparty/icewind/smb/src/Exception/FileInUseException.php
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* This file is licensed under the Licensed under the MIT license:
|
||||
* http://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class FileInUseException extends InvalidRequestException {}
|
10
apps/files_external/3rdparty/icewind/smb/src/Exception/ForbiddenException.php
vendored
Normal file
10
apps/files_external/3rdparty/icewind/smb/src/Exception/ForbiddenException.php
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* This file is licensed under the Licensed under the MIT license:
|
||||
* http://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class ForbiddenException extends InvalidRequestException {}
|
11
apps/files_external/3rdparty/icewind/smb/src/Exception/HostDownException.php
vendored
Normal file
11
apps/files_external/3rdparty/icewind/smb/src/Exception/HostDownException.php
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* This file is licensed under the Licensed under the MIT license:
|
||||
* http://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class HostDownException extends ConnectException {
|
||||
}
|
10
apps/files_external/3rdparty/icewind/smb/src/Exception/InvalidHostException.php
vendored
Normal file
10
apps/files_external/3rdparty/icewind/smb/src/Exception/InvalidHostException.php
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* This file is licensed under the Licensed under the MIT license:
|
||||
* http://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class InvalidHostException extends ConnectException {}
|
31
apps/files_external/3rdparty/icewind/smb/src/Exception/InvalidRequestException.php
vendored
Normal file
31
apps/files_external/3rdparty/icewind/smb/src/Exception/InvalidRequestException.php
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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;
|
||||
}
|
||||
}
|
10
apps/files_external/3rdparty/icewind/smb/src/Exception/InvalidTypeException.php
vendored
Normal file
10
apps/files_external/3rdparty/icewind/smb/src/Exception/InvalidTypeException.php
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* This file is licensed under the Licensed under the MIT license:
|
||||
* http://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class InvalidTypeException extends InvalidRequestException {}
|
11
apps/files_external/3rdparty/icewind/smb/src/Exception/NoRouteToHostException.php
vendored
Normal file
11
apps/files_external/3rdparty/icewind/smb/src/Exception/NoRouteToHostException.php
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* This file is licensed under the Licensed under the MIT license:
|
||||
* http://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class NoRouteToHostException extends ConnectException {
|
||||
}
|
10
apps/files_external/3rdparty/icewind/smb/src/Exception/NotEmptyException.php
vendored
Normal file
10
apps/files_external/3rdparty/icewind/smb/src/Exception/NotEmptyException.php
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* This file is licensed under the Licensed under the MIT license:
|
||||
* http://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class NotEmptyException extends InvalidRequestException {}
|
10
apps/files_external/3rdparty/icewind/smb/src/Exception/NotFoundException.php
vendored
Normal file
10
apps/files_external/3rdparty/icewind/smb/src/Exception/NotFoundException.php
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* This file is licensed under the Licensed under the MIT license:
|
||||
* http://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class NotFoundException extends InvalidRequestException {}
|
11
apps/files_external/3rdparty/icewind/smb/src/Exception/TimedOutException.php
vendored
Normal file
11
apps/files_external/3rdparty/icewind/smb/src/Exception/TimedOutException.php
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* This file is licensed under the Licensed under the MIT license:
|
||||
* http://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class TimedOutException extends ConnectException {
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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();
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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);
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,288 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,308 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,394 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,534 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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, ''));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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';
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"host": "localhost",
|
||||
"user": "test",
|
||||
"password": "test",
|
||||
"share": "test",
|
||||
"root": "test"
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<phpunit bootstrap="bootstrap.php">
|
||||
<testsuite name='SMB'>
|
||||
<directory suffix='.php'>./</directory>
|
||||
</testsuite>
|
||||
</phpunit>
|
|
@ -0,0 +1,3 @@
|
|||
.idea
|
||||
vendor
|
||||
composer.lock
|
|
@ -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
|
|
@ -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
|
||||
<?php
|
||||
|
||||
use \Icewind\Streams\CallBackWrapper;
|
||||
|
||||
require('vendor/autoload.php');
|
||||
|
||||
// get an existing stream to wrap
|
||||
$source = fopen('php://temp', 'r+');
|
||||
|
||||
// register the callbacks
|
||||
$stream = CallbackWrapper::wrap($source,
|
||||
// read callback
|
||||
function ($count) {
|
||||
echo "read " . $count . "bytes\n";
|
||||
},
|
||||
// write callback
|
||||
function ($data) {
|
||||
echo "wrote '" . $data . "'\n";
|
||||
},
|
||||
// close callback
|
||||
function () {
|
||||
echo "stream closed\n";
|
||||
});
|
||||
|
||||
fwrite($stream, 'some dummy data');
|
||||
|
||||
rewind($stream);
|
||||
fread($stream, 5);
|
||||
|
||||
fclose($stream);
|
||||
```
|
||||
|
||||
Note: due to php's internal stream buffering the `$count` passed to the read callback
|
||||
will be equal to php's internal buffer size (8192 on default) an not the number of bytes
|
||||
requested by `fopen()`
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name" : "icewind/streams",
|
||||
"description" : "A set of generic stream wrappers",
|
||||
"license" : "MIT",
|
||||
"authors" : [
|
||||
{
|
||||
"name" : "Robin Appelman",
|
||||
"email": "icewind@owncloud.com"
|
||||
}
|
||||
],
|
||||
"require" : {
|
||||
"php": ">=5.3"
|
||||
},
|
||||
"require-dev" : {
|
||||
"satooshi/php-coveralls": "dev-master"
|
||||
},
|
||||
"autoload" : {
|
||||
"psr-4": {
|
||||
"Icewind\\Streams\\Tests\\": "tests/",
|
||||
"Icewind\\Streams\\": "src/"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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();
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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();
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* 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';
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<phpunit bootstrap="bootstrap.php">
|
||||
<testsuite name='Stream'>
|
||||
<directory suffix='.php'>./</directory>
|
||||
</testsuite>
|
||||
</phpunit>
|
|
@ -1,516 +0,0 @@
|
|||
<?php
|
||||
###################################################################
|
||||
# smb.php
|
||||
# This class implements a SMB stream wrapper based on 'smbclient'
|
||||
#
|
||||
# Date: lun oct 22 10:35:35 CEST 2007
|
||||
#
|
||||
# Homepage: http://www.phpclasses.org/smb4php
|
||||
#
|
||||
# Copyright (c) 2007 Victor M. Varela <vmvarela@gmail.com>
|
||||
# Copyright (c) 2012 Frank Karlitschek <frank@owncloud.org>
|
||||
# Copyright (c) 2014 Robin McCorkell <rmccorkell@karoshi.org.uk>
|
||||
#
|
||||
# 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');
|
|
@ -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');
|
||||
|
|
|
@ -8,139 +8,273 @@
|
|||
|
||||
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 (Server::NativeAvailable()) {
|
||||
$this->server = new NativeServer($params['host'], $params['user'], $params['password']);
|
||||
} else {
|
||||
$this->server = new Server($params['host'], $params['user'], $params['password']);
|
||||
}
|
||||
if (substr($this->root, -1, 1)!='/') {
|
||||
$this->root.='/';
|
||||
$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;
|
||||
}
|
||||
if ( ! $this->share || $this->share[0]!='/') {
|
||||
$this->share='/'.$this->share;
|
||||
}
|
||||
if (substr($this->share, -1, 1)=='/') {
|
||||
$this->share = substr($this->share, 0, -1);
|
||||
if (substr($this->root, -1, 1) != '/') {
|
||||
$this->root .= '/';
|
||||
}
|
||||
} else {
|
||||
throw new \Exception('Invalid configuration');
|
||||
}
|
||||
}
|
||||
|
||||
public function getId(){
|
||||
return 'smb::' . $this->user . '@' . $this->host . '/' . $this->share . '/' . $this->root;
|
||||
}
|
||||
|
||||
public function constructUrl($path) {
|
||||
if (substr($path, -1)=='/') {
|
||||
$path = substr($path, 0, -1);
|
||||
}
|
||||
if (substr($path, 0, 1)=='/') {
|
||||
$path = substr($path, 1);
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
// smb4php can return an empty array if the connection could not be established
|
||||
if (empty($stat)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $stat;
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getId() {
|
||||
return 'smb::' . $this->server->getUser() . '@' . $this->server->getHost() . '/' . $this->share->getName() . '/' . $this->root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlinks file or directory
|
||||
* @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);
|
||||
}
|
||||
return $this->statCache[$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Icewind\SMB\IFileInfo $info
|
||||
* @return array
|
||||
*/
|
||||
protected function formatInfo($info) {
|
||||
return array(
|
||||
'size' => $info->getSize(),
|
||||
'mtime' => $info->getMTime()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @return array
|
||||
*/
|
||||
public function stat($path) {
|
||||
return $this->formatInfo($this->getFileInfo($path));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
public function unlink($path) {
|
||||
if ($this->is_dir($path)) {
|
||||
$this->rmdir($path);
|
||||
try {
|
||||
if ($this->is_dir($path)) {
|
||||
return $this->rmdir($path);
|
||||
} else {
|
||||
$path = $this->buildPath($path);
|
||||
unset($this->statCache[$path]);
|
||||
$this->share->del($path);
|
||||
return true;
|
||||
}
|
||||
} catch (NotFoundException $e) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
$url = $this->constructUrl($path);
|
||||
unlink($url);
|
||||
clearstatcache(false, $url);
|
||||
}
|
||||
// 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');
|
||||
$smbClientExists = (bool)\OC_Helper::findBinaryPath('smbclient');
|
||||
return $smbClientExists ? true : array('smbclient');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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(''));
|
||||
$server = new Server($this->server->getHost(), '', '');
|
||||
|
||||
// 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();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,6 @@ namespace Test\Files\Storage;
|
|||
|
||||
class SMB extends Storage {
|
||||
|
||||
private $config;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
|
@ -20,6 +18,9 @@ class SMB extends Storage {
|
|||
if (!is_array($config) or !$config['run']) {
|
||||
$this->markTestSkipped('Samba backend not configured');
|
||||
}
|
||||
if (substr($config['root'], -1, 1) != '/') {
|
||||
$config['root'] .= '/';
|
||||
}
|
||||
$config['root'] .= $id; //make sure we have an new empty folder to work in
|
||||
$this->instance = new \OC\Files\Storage\SMB($config);
|
||||
$this->instance->mkdir('/');
|
||||
|
@ -27,7 +28,7 @@ class SMB extends Storage {
|
|||
|
||||
protected function tearDown() {
|
||||
if ($this->instance) {
|
||||
\OCP\Files::rmdirr($this->instance->constructUrl(''));
|
||||
$this->instance->rmdir('');
|
||||
}
|
||||
|
||||
parent::tearDown();
|
||||
|
|
|
@ -10,8 +10,6 @@ namespace Test\Files\Storage;
|
|||
|
||||
class DAV extends Storage {
|
||||
|
||||
private $config;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2013 Vincent Petry <pvince81@owncloud.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
namespace Test\Files\Storage;
|
||||
|
||||
class SMBFunctions extends \Test\TestCase {
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// dummy config
|
||||
$this->config = array(
|
||||
'run'=>false,
|
||||
'user'=>'test',
|
||||
'password'=>'testpassword',
|
||||
'host'=>'smbhost',
|
||||
'share'=>'/sharename',
|
||||
'root'=>'/rootdir/',
|
||||
);
|
||||
|
||||
$this->instance = new \OC\Files\Storage\SMB($this->config);
|
||||
}
|
||||
|
||||
public function testGetId() {
|
||||
$this->assertEquals('smb::test@smbhost//sharename//rootdir/', $this->instance->getId());
|
||||
}
|
||||
|
||||
public function testConstructUrl() {
|
||||
$this->assertEquals("smb://test:testpassword@smbhost/sharename/rootdir/abc", $this->instance->constructUrl('/abc'));
|
||||
$this->assertEquals("smb://test:testpassword@smbhost/sharename/rootdir/abc", $this->instance->constructUrl('/abc/'));
|
||||
$this->assertEquals("smb://test:testpassword@smbhost/sharename/rootdir/abc%2F", $this->instance->constructUrl('/abc/.'));
|
||||
$this->assertEquals("smb://test:testpassword@smbhost/sharename/rootdir/abc%2Fdef", $this->instance->constructUrl('/abc/def'));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue