Merge pull request #14064 from nextcloud/smb-3.1.0

update icewind/smb to 3.1.0
This commit is contained in:
Morris Jobke 2019-02-07 16:46:31 +01:00 committed by GitHub
commit c5dc9b57ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
54 changed files with 547 additions and 198 deletions

View file

@ -1,4 +1,5 @@
example.php example.php
.editorconfig
icewind/smb/tests icewind/smb/tests
icewind/smb/install_libsmbclient.sh icewind/smb/install_libsmbclient.sh
icewind/smb/Makefile icewind/smb/Makefile

View file

@ -9,6 +9,6 @@
}, },
"require": { "require": {
"icewind/streams": "0.6.1", "icewind/streams": "0.6.1",
"icewind/smb": "3.0.0" "icewind/smb": "3.1.0"
} }
} }

View file

@ -4,20 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "1465c8a4b4139e514086d5803d90af2d", "content-hash": "ea11a8c6e4979d35dd3bdf1f057ef0e8",
"packages": [ "packages": [
{ {
"name": "icewind/smb", "name": "icewind/smb",
"version": "v3.0.0", "version": "v3.1.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/icewind1991/SMB.git", "url": "https://github.com/icewind1991/SMB.git",
"reference": "0d31da4757a37d322e1e181f2286e8a4c89fbc0c" "reference": "db16d4430cb75f0196eaa6377c5766b19f744c8d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/icewind1991/SMB/zipball/0d31da4757a37d322e1e181f2286e8a4c89fbc0c", "url": "https://api.github.com/repos/icewind1991/SMB/zipball/db16d4430cb75f0196eaa6377c5766b19f744c8d",
"reference": "0d31da4757a37d322e1e181f2286e8a4c89fbc0c", "reference": "db16d4430cb75f0196eaa6377c5766b19f744c8d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -25,6 +25,7 @@
"php": ">=5.6" "php": ">=5.6"
}, },
"require-dev": { "require-dev": {
"friendsofphp/php-cs-fixer": "^2.13",
"phpunit/phpunit": "^5.7" "phpunit/phpunit": "^5.7"
}, },
"type": "library", "type": "library",
@ -45,7 +46,7 @@
} }
], ],
"description": "php wrapper for smbclient and libsmbclient-php", "description": "php wrapper for smbclient and libsmbclient-php",
"time": "2018-05-24T09:48:51+00:00" "time": "2019-02-06T14:17:35+00:00"
}, },
{ {
"name": "icewind/streams", "name": "icewind/streams",

View file

@ -279,7 +279,7 @@ class ClassLoader
*/ */
public function setApcuPrefix($apcuPrefix) public function setApcuPrefix($apcuPrefix)
{ {
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
} }
/** /**
@ -377,7 +377,7 @@ class ClassLoader
$subPath = $class; $subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) { while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos); $subPath = substr($subPath, 0, $lastPos);
$search = $subPath.'\\'; $search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) { if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) { foreach ($this->prefixDirsPsr4[$search] as $dir) {

View file

@ -39,8 +39,11 @@ return array(
'Icewind\\SMB\\IAuth' => $vendorDir . '/icewind/smb/src/IAuth.php', 'Icewind\\SMB\\IAuth' => $vendorDir . '/icewind/smb/src/IAuth.php',
'Icewind\\SMB\\IFileInfo' => $vendorDir . '/icewind/smb/src/IFileInfo.php', 'Icewind\\SMB\\IFileInfo' => $vendorDir . '/icewind/smb/src/IFileInfo.php',
'Icewind\\SMB\\INotifyHandler' => $vendorDir . '/icewind/smb/src/INotifyHandler.php', 'Icewind\\SMB\\INotifyHandler' => $vendorDir . '/icewind/smb/src/INotifyHandler.php',
'Icewind\\SMB\\IOptions' => $vendorDir . '/icewind/smb/src/IOptions.php',
'Icewind\\SMB\\IServer' => $vendorDir . '/icewind/smb/src/IServer.php', 'Icewind\\SMB\\IServer' => $vendorDir . '/icewind/smb/src/IServer.php',
'Icewind\\SMB\\IShare' => $vendorDir . '/icewind/smb/src/IShare.php', 'Icewind\\SMB\\IShare' => $vendorDir . '/icewind/smb/src/IShare.php',
'Icewind\\SMB\\ISystem' => $vendorDir . '/icewind/smb/src/ISystem.php',
'Icewind\\SMB\\ITimeZoneProvider' => $vendorDir . '/icewind/smb/src/ITimeZoneProvider.php',
'Icewind\\SMB\\KerberosAuth' => $vendorDir . '/icewind/smb/src/KerberosAuth.php', 'Icewind\\SMB\\KerberosAuth' => $vendorDir . '/icewind/smb/src/KerberosAuth.php',
'Icewind\\SMB\\Native\\NativeFileInfo' => $vendorDir . '/icewind/smb/src/Native/NativeFileInfo.php', 'Icewind\\SMB\\Native\\NativeFileInfo' => $vendorDir . '/icewind/smb/src/Native/NativeFileInfo.php',
'Icewind\\SMB\\Native\\NativeReadStream' => $vendorDir . '/icewind/smb/src/Native/NativeReadStream.php', 'Icewind\\SMB\\Native\\NativeReadStream' => $vendorDir . '/icewind/smb/src/Native/NativeReadStream.php',
@ -49,6 +52,7 @@ return array(
'Icewind\\SMB\\Native\\NativeState' => $vendorDir . '/icewind/smb/src/Native/NativeState.php', 'Icewind\\SMB\\Native\\NativeState' => $vendorDir . '/icewind/smb/src/Native/NativeState.php',
'Icewind\\SMB\\Native\\NativeStream' => $vendorDir . '/icewind/smb/src/Native/NativeStream.php', 'Icewind\\SMB\\Native\\NativeStream' => $vendorDir . '/icewind/smb/src/Native/NativeStream.php',
'Icewind\\SMB\\Native\\NativeWriteStream' => $vendorDir . '/icewind/smb/src/Native/NativeWriteStream.php', 'Icewind\\SMB\\Native\\NativeWriteStream' => $vendorDir . '/icewind/smb/src/Native/NativeWriteStream.php',
'Icewind\\SMB\\Options' => $vendorDir . '/icewind/smb/src/Options.php',
'Icewind\\SMB\\ServerFactory' => $vendorDir . '/icewind/smb/src/ServerFactory.php', 'Icewind\\SMB\\ServerFactory' => $vendorDir . '/icewind/smb/src/ServerFactory.php',
'Icewind\\SMB\\System' => $vendorDir . '/icewind/smb/src/System.php', 'Icewind\\SMB\\System' => $vendorDir . '/icewind/smb/src/System.php',
'Icewind\\SMB\\TimeZoneProvider' => $vendorDir . '/icewind/smb/src/TimeZoneProvider.php', 'Icewind\\SMB\\TimeZoneProvider' => $vendorDir . '/icewind/smb/src/TimeZoneProvider.php',

View file

@ -69,8 +69,11 @@ class ComposerStaticInit98fe9b281934250b3a93f69a5ce843b3
'Icewind\\SMB\\IAuth' => __DIR__ . '/..' . '/icewind/smb/src/IAuth.php', 'Icewind\\SMB\\IAuth' => __DIR__ . '/..' . '/icewind/smb/src/IAuth.php',
'Icewind\\SMB\\IFileInfo' => __DIR__ . '/..' . '/icewind/smb/src/IFileInfo.php', 'Icewind\\SMB\\IFileInfo' => __DIR__ . '/..' . '/icewind/smb/src/IFileInfo.php',
'Icewind\\SMB\\INotifyHandler' => __DIR__ . '/..' . '/icewind/smb/src/INotifyHandler.php', 'Icewind\\SMB\\INotifyHandler' => __DIR__ . '/..' . '/icewind/smb/src/INotifyHandler.php',
'Icewind\\SMB\\IOptions' => __DIR__ . '/..' . '/icewind/smb/src/IOptions.php',
'Icewind\\SMB\\IServer' => __DIR__ . '/..' . '/icewind/smb/src/IServer.php', 'Icewind\\SMB\\IServer' => __DIR__ . '/..' . '/icewind/smb/src/IServer.php',
'Icewind\\SMB\\IShare' => __DIR__ . '/..' . '/icewind/smb/src/IShare.php', 'Icewind\\SMB\\IShare' => __DIR__ . '/..' . '/icewind/smb/src/IShare.php',
'Icewind\\SMB\\ISystem' => __DIR__ . '/..' . '/icewind/smb/src/ISystem.php',
'Icewind\\SMB\\ITimeZoneProvider' => __DIR__ . '/..' . '/icewind/smb/src/ITimeZoneProvider.php',
'Icewind\\SMB\\KerberosAuth' => __DIR__ . '/..' . '/icewind/smb/src/KerberosAuth.php', 'Icewind\\SMB\\KerberosAuth' => __DIR__ . '/..' . '/icewind/smb/src/KerberosAuth.php',
'Icewind\\SMB\\Native\\NativeFileInfo' => __DIR__ . '/..' . '/icewind/smb/src/Native/NativeFileInfo.php', 'Icewind\\SMB\\Native\\NativeFileInfo' => __DIR__ . '/..' . '/icewind/smb/src/Native/NativeFileInfo.php',
'Icewind\\SMB\\Native\\NativeReadStream' => __DIR__ . '/..' . '/icewind/smb/src/Native/NativeReadStream.php', 'Icewind\\SMB\\Native\\NativeReadStream' => __DIR__ . '/..' . '/icewind/smb/src/Native/NativeReadStream.php',
@ -79,6 +82,7 @@ class ComposerStaticInit98fe9b281934250b3a93f69a5ce843b3
'Icewind\\SMB\\Native\\NativeState' => __DIR__ . '/..' . '/icewind/smb/src/Native/NativeState.php', 'Icewind\\SMB\\Native\\NativeState' => __DIR__ . '/..' . '/icewind/smb/src/Native/NativeState.php',
'Icewind\\SMB\\Native\\NativeStream' => __DIR__ . '/..' . '/icewind/smb/src/Native/NativeStream.php', 'Icewind\\SMB\\Native\\NativeStream' => __DIR__ . '/..' . '/icewind/smb/src/Native/NativeStream.php',
'Icewind\\SMB\\Native\\NativeWriteStream' => __DIR__ . '/..' . '/icewind/smb/src/Native/NativeWriteStream.php', 'Icewind\\SMB\\Native\\NativeWriteStream' => __DIR__ . '/..' . '/icewind/smb/src/Native/NativeWriteStream.php',
'Icewind\\SMB\\Options' => __DIR__ . '/..' . '/icewind/smb/src/Options.php',
'Icewind\\SMB\\ServerFactory' => __DIR__ . '/..' . '/icewind/smb/src/ServerFactory.php', 'Icewind\\SMB\\ServerFactory' => __DIR__ . '/..' . '/icewind/smb/src/ServerFactory.php',
'Icewind\\SMB\\System' => __DIR__ . '/..' . '/icewind/smb/src/System.php', 'Icewind\\SMB\\System' => __DIR__ . '/..' . '/icewind/smb/src/System.php',
'Icewind\\SMB\\TimeZoneProvider' => __DIR__ . '/..' . '/icewind/smb/src/TimeZoneProvider.php', 'Icewind\\SMB\\TimeZoneProvider' => __DIR__ . '/..' . '/icewind/smb/src/TimeZoneProvider.php',

View file

@ -1,17 +1,17 @@
[ [
{ {
"name": "icewind/smb", "name": "icewind/smb",
"version": "v3.0.0", "version": "v3.1.0",
"version_normalized": "3.0.0.0", "version_normalized": "3.1.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/icewind1991/SMB.git", "url": "https://github.com/icewind1991/SMB.git",
"reference": "0d31da4757a37d322e1e181f2286e8a4c89fbc0c" "reference": "db16d4430cb75f0196eaa6377c5766b19f744c8d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/icewind1991/SMB/zipball/0d31da4757a37d322e1e181f2286e8a4c89fbc0c", "url": "https://api.github.com/repos/icewind1991/SMB/zipball/db16d4430cb75f0196eaa6377c5766b19f744c8d",
"reference": "0d31da4757a37d322e1e181f2286e8a4c89fbc0c", "reference": "db16d4430cb75f0196eaa6377c5766b19f744c8d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -19,9 +19,10 @@
"php": ">=5.6" "php": ">=5.6"
}, },
"require-dev": { "require-dev": {
"friendsofphp/php-cs-fixer": "^2.13",
"phpunit/phpunit": "^5.7" "phpunit/phpunit": "^5.7"
}, },
"time": "2018-05-24T09:48:51+00:00", "time": "2019-02-06T14:17:35+00:00",
"type": "library", "type": "library",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {

View file

@ -1,3 +1,5 @@
.idea .idea
vendor vendor
composer.lock composer.lock
.php_cs.cache

View file

@ -0,0 +1,15 @@
<?php
$finder = PhpCsFixer\Finder::create()
->exclude('vendor')
->in(__DIR__)
;
return PhpCsFixer\Config::create()
->setRules([
'@PSR2' => true,
'array_syntax' => ['syntax' => 'short'],
'braces' => ['position_after_functions_and_oop_constructs' => 'same'],
'binary_operator_spaces' => ['align_double_arrow' => true, 'align_equals' => false],
])
->setIndent("\t")
->setFinder($finder)
;

View file

@ -102,6 +102,15 @@ fwrite($fh, 'bar');
fclose($fh); fclose($fh);
``` ```
**Note**: write() will truncate your file to 0bytes. You may open a writeable stream with append() which will point
the cursor to the end of the file or create it if it does not exists yet. (append() is only compatible with libsmbclient-php)
```php
$fh = $share->append('test.txt');
fwrite($fh, 'bar');
fclose($fh);
```
### Using notify ### Using notify
```php ```php
@ -110,6 +119,22 @@ $share->notify('')->listen(function (\Icewind\SMB\Change $change) {
}); });
``` ```
### Changing network timeouts
```php
$options = new Options();
$options->setTimeout(5);
$serverFactory = new ServerFactory($options);
```
### Customizing system integration
The `smbclient` backend needs to get various information about the system it's running on to function
such as the paths of various binaries or the system timezone.
While the default logic for getting this information should work on most systems, it possible to customize this behaviour.
In order to customize the integration you provide a custom implementation of `ITimezoneProvider` and/or `ISystem` and pass them as arguments to the `ServerFactory`.
## Testing SMB ## Testing SMB
Use the following steps to check if the library can connect to your SMB share. Use the following steps to check if the library can connect to your SMB share.

View file

@ -13,7 +13,8 @@
"icewind/streams": ">=0.2.0" "icewind/streams": ">=0.2.0"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^5.7" "phpunit/phpunit": "^5.7",
"friendsofphp/php-cs-fixer": "^2.13"
}, },
"autoload" : { "autoload" : {
"psr-4": { "psr-4": {

View file

@ -21,7 +21,6 @@
namespace Icewind\SMB; namespace Icewind\SMB;
abstract class AbstractServer implements IServer { abstract class AbstractServer implements IServer {
const LOCALE = 'en_US.UTF-8'; const LOCALE = 'en_US.UTF-8';
@ -36,7 +35,7 @@ abstract class AbstractServer implements IServer {
protected $auth; protected $auth;
/** /**
* @var \Icewind\SMB\System * @var ISystem
*/ */
protected $system; protected $system;
@ -45,41 +44,41 @@ abstract class AbstractServer implements IServer {
*/ */
protected $timezoneProvider; protected $timezoneProvider;
/** @var IOptions */
protected $options;
/** /**
* @param string $host * @param string $host
* @param IAuth $auth * @param IAuth $auth
* @param System $system * @param ISystem $system
* @param TimeZoneProvider $timeZoneProvider * @param TimeZoneProvider $timeZoneProvider
* @param IOptions $options
*/ */
public function __construct($host, IAuth $auth, System $system, TimeZoneProvider $timeZoneProvider) { public function __construct($host, IAuth $auth, ISystem $system, TimeZoneProvider $timeZoneProvider, IOptions $options) {
$this->host = $host; $this->host = $host;
$this->auth = $auth; $this->auth = $auth;
$this->system = $system; $this->system = $system;
$this->timezoneProvider = $timeZoneProvider; $this->timezoneProvider = $timeZoneProvider;
$this->options = $options;
} }
/**
* @return IAuth
*/
public function getAuth() { public function getAuth() {
return $this->auth; return $this->auth;
} }
/**
* return string
*/
public function getHost() { public function getHost() {
return $this->host; return $this->host;
} }
/**
* @return string
*/
public function getTimeZone() { public function getTimeZone() {
return $this->timezoneProvider->get(); return $this->timezoneProvider->get($this->host);
} }
public function getSystem() { public function getSystem() {
return $this->system; return $this->system;
} }
public function getOptions() {
return $this->options;
}
} }

View file

@ -13,7 +13,7 @@ abstract class AbstractShare implements IShare {
private $forbiddenCharacters; private $forbiddenCharacters;
public function __construct() { public function __construct() {
$this->forbiddenCharacters = array('?', '<', '>', ':', '*', '|', '"', chr(0), "\n", "\r"); $this->forbiddenCharacters = ['?', '<', '>', ':', '*', '|', '"', chr(0), "\n", "\r"];
} }
protected function verifyPath($path) { protected function verifyPath($path) {

View file

@ -21,7 +21,6 @@
namespace Icewind\SMB; namespace Icewind\SMB;
class AnonymousAuth implements IAuth { class AnonymousAuth implements IAuth {
public function getUsername() { public function getUsername() {
return null; return null;

View file

@ -21,7 +21,6 @@
namespace Icewind\SMB; namespace Icewind\SMB;
class BasicAuth implements IAuth { class BasicAuth implements IAuth {
/** @var string */ /** @var string */
private $username; private $username;
@ -62,5 +61,4 @@ class BasicAuth implements IAuth {
public function setExtraSmbClientOptions($smbClientState) { public function setExtraSmbClientOptions($smbClientState) {
// noop // noop
} }
} }

View file

@ -7,4 +7,5 @@
namespace Icewind\SMB\Exception; namespace Icewind\SMB\Exception;
class AccessDeniedException extends ConnectException {} class AccessDeniedException extends ConnectException {
}

View file

@ -7,4 +7,5 @@
namespace Icewind\SMB\Exception; namespace Icewind\SMB\Exception;
class AlreadyExistsException extends InvalidRequestException {} class AlreadyExistsException extends InvalidRequestException {
}

View file

@ -7,4 +7,5 @@
namespace Icewind\SMB\Exception; namespace Icewind\SMB\Exception;
class AuthenticationException extends ConnectException{} class AuthenticationException extends ConnectException {
}

View file

@ -7,4 +7,5 @@
namespace Icewind\SMB\Exception; namespace Icewind\SMB\Exception;
class ConnectException extends Exception {} class ConnectException extends Exception {
}

View file

@ -7,4 +7,5 @@
namespace Icewind\SMB\Exception; namespace Icewind\SMB\Exception;
class ConnectionException extends ConnectException {} class ConnectionException extends ConnectException {
}

View file

@ -8,7 +8,7 @@
namespace Icewind\SMB\Exception; namespace Icewind\SMB\Exception;
class Exception extends \Exception { class Exception extends \Exception {
static public function unknown($path, $error) { public static function unknown($path, $error) {
$message = 'Unknown error (' . $error . ')'; $message = 'Unknown error (' . $error . ')';
if ($path) { if ($path) {
$message .= ' for ' . $path; $message .= ' for ' . $path;
@ -23,7 +23,7 @@ class Exception extends \Exception {
* @param string $path * @param string $path
* @return Exception * @return Exception
*/ */
static public function fromMap(array $exceptionMap, $error, $path) { public static function fromMap(array $exceptionMap, $error, $path) {
if (isset($exceptionMap[$error])) { if (isset($exceptionMap[$error])) {
$exceptionClass = $exceptionMap[$error]; $exceptionClass = $exceptionMap[$error];
if (is_numeric($error)) { if (is_numeric($error)) {

View file

@ -7,4 +7,5 @@
namespace Icewind\SMB\Exception; namespace Icewind\SMB\Exception;
class FileInUseException extends InvalidRequestException {} class FileInUseException extends InvalidRequestException {
}

View file

@ -7,4 +7,5 @@
namespace Icewind\SMB\Exception; namespace Icewind\SMB\Exception;
class ForbiddenException extends InvalidRequestException {} class ForbiddenException extends InvalidRequestException {
}

View file

@ -7,4 +7,5 @@
namespace Icewind\SMB\Exception; namespace Icewind\SMB\Exception;
class InvalidArgumentException extends InvalidRequestException {} class InvalidArgumentException extends InvalidRequestException {
}

View file

@ -7,4 +7,5 @@
namespace Icewind\SMB\Exception; namespace Icewind\SMB\Exception;
class InvalidHostException extends ConnectException {} class InvalidHostException extends ConnectException {
}

View file

@ -7,4 +7,5 @@
namespace Icewind\SMB\Exception; namespace Icewind\SMB\Exception;
class InvalidParameterException extends InvalidRequestException {} class InvalidParameterException extends InvalidRequestException {
}

View file

@ -7,4 +7,5 @@
namespace Icewind\SMB\Exception; namespace Icewind\SMB\Exception;
class InvalidPathException extends InvalidRequestException {} class InvalidPathException extends InvalidRequestException {
}

View file

@ -7,4 +7,5 @@
namespace Icewind\SMB\Exception; namespace Icewind\SMB\Exception;
class InvalidTypeException extends InvalidRequestException {} class InvalidTypeException extends InvalidRequestException {
}

View file

@ -7,4 +7,5 @@
namespace Icewind\SMB\Exception; namespace Icewind\SMB\Exception;
class NoLoginServerException extends ConnectException {} class NoLoginServerException extends ConnectException {
}

View file

@ -7,4 +7,5 @@
namespace Icewind\SMB\Exception; namespace Icewind\SMB\Exception;
class NotEmptyException extends InvalidRequestException {} class NotEmptyException extends InvalidRequestException {
}

View file

@ -7,4 +7,5 @@
namespace Icewind\SMB\Exception; namespace Icewind\SMB\Exception;
class NotFoundException extends InvalidRequestException {} class NotFoundException extends InvalidRequestException {
}

View file

@ -8,7 +8,6 @@
namespace Icewind\SMB; namespace Icewind\SMB;
interface INotifyHandler { interface INotifyHandler {
// https://msdn.microsoft.com/en-us/library/dn392331.aspx // https://msdn.microsoft.com/en-us/library/dn392331.aspx
const NOTIFY_ADDED = 1; const NOTIFY_ADDED = 1;

View file

@ -0,0 +1,29 @@
<?php
/**
* @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace Icewind\SMB;
interface IOptions {
/**
* @return int
*/
public function getTimeout();
}

View file

@ -52,13 +52,18 @@ interface IServer {
public function getTimeZone(); public function getTimeZone();
/** /**
* @return System * @return ISystem
*/ */
public function getSystem(); public function getSystem();
/** /**
* @param System $system * @return IOptions
*/
public function getOptions();
/**
* @param ISystem $system
* @return bool * @return bool
*/ */
public static function available(System $system); public static function available(ISystem $system);
} }

View file

@ -52,6 +52,7 @@ interface IShare {
/** /**
* Open a writable stream to a remote file * Open a writable stream to a remote file
* Note: This method will truncate the file to 0bytes
* *
* @param string $target * @param string $target
* @return resource a write only stream to upload a remote file * @return resource a write only stream to upload a remote file
@ -61,6 +62,18 @@ interface IShare {
*/ */
public function write($target); public function write($target);
/**
* Open a writable stream to a remote file and set the cursor to the end of the 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
* @throws \Icewind\SMB\Exception\InvalidRequestException
*/
public function append($target);
/** /**
* Rename a remote file * Rename a remote file
* *

View file

@ -0,0 +1,71 @@
<?php
/**
* @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace Icewind\SMB;
/**
* The `ISystem` interface provides a way to access system dependent information
* such as the availability and location of certain binaries.
*/
interface ISystem {
/**
* Get the path to a file descriptor of the current process
*
* @param int $num the file descriptor id
* @return string
*/
public function getFD($num);
/**
* Get the full path to the `smbclient` binary of false if the binary is not available
*
* @return string|bool
*/
public function getSmbclientPath();
/**
* Get the full path to the `net` binary of false if the binary is not available
*
* @return string|bool
*/
public function getNetPath();
/**
* Get the full path to the `stdbuf` binary of false if the binary is not available
*
* @return string|bool
*/
public function getStdBufPath();
/**
* Get the full path to the `date` binary of false if the binary is not available
*
* @return string|bool
*/
public function getDatePath();
/**
* Whether or not the smbclient php extension is enabled
*
* @return bool
*/
public function libSmbclientAvailable();
}

View file

@ -0,0 +1,32 @@
<?php
/**
* @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace Icewind\SMB;
interface ITimeZoneProvider {
/**
* Get the timezone of the smb server
*
* @param string $host
* @return string
*/
public function get($host);
}

View file

@ -45,5 +45,4 @@ class KerberosAuth implements IAuth {
smbclient_option_set($smbClientState, SMBCLIENT_OPT_USE_KERBEROS, true); smbclient_option_set($smbClientState, SMBCLIENT_OPT_USE_KERBEROS, true);
smbclient_option_set($smbClientState, SMBCLIENT_OPT_FALLBACK_AFTER_KERBEROS, false); smbclient_option_set($smbClientState, SMBCLIENT_OPT_FALLBACK_AFTER_KERBEROS, false);
} }
} }

View file

@ -30,7 +30,10 @@ class NativeFileInfo implements IFileInfo {
/** /**
* @var array|null * @var array|null
*/ */
protected $statCache; protected $statCache = null;
/** @var callable|null */
protected $statCallback = null;
/** /**
* @var int * @var int
@ -41,13 +44,20 @@ class NativeFileInfo implements IFileInfo {
* @param NativeShare $share * @param NativeShare $share
* @param string $path * @param string $path
* @param string $name * @param string $name
* @param array $stat * @param array|callable $stat
*/ */
public function __construct($share, $path, $name, $stat = null) { public function __construct($share, $path, $name, $stat) {
$this->share = $share; $this->share = $share;
$this->path = $path; $this->path = $path;
$this->name = $name; $this->name = $name;
$this->statCache = $stat;
if (is_array($stat)) {
$this->statCache = $stat;
} elseif (is_callable($stat)) {
$this->statCallback = $stat;
} else {
throw new \InvalidArgumentException('$stat needs to be an array or callback');
}
} }
/** /**
@ -69,7 +79,7 @@ class NativeFileInfo implements IFileInfo {
*/ */
protected function stat() { protected function stat() {
if (is_null($this->statCache)) { if (is_null($this->statCache)) {
$this->statCache = $this->share->getStat($this->getPath()); $this->statCache = call_user_func($this->statCallback);
} }
return $this->statCache; return $this->statCache;
} }

View file

@ -25,7 +25,6 @@ class NativeReadStream extends NativeStream {
$this->readBuffer = fopen('php://memory', 'r+'); $this->readBuffer = fopen('php://memory', 'r+');
return parent::stream_open($path, $mode, $options, $opened_path); return parent::stream_open($path, $mode, $options, $opened_path);
} }
/** /**
@ -39,13 +38,13 @@ class NativeReadStream extends NativeStream {
*/ */
public static function wrap($state, $smbStream, $mode, $url) { public static function wrap($state, $smbStream, $mode, $url) {
stream_wrapper_register('nativesmb', NativeReadStream::class); stream_wrapper_register('nativesmb', NativeReadStream::class);
$context = stream_context_create(array( $context = stream_context_create([
'nativesmb' => array( 'nativesmb' => [
'state' => $state, 'state' => $state,
'handle' => $smbStream, 'handle' => $smbStream,
'url' => $url 'url' => $url
) ]
)); ]);
$fh = fopen('nativesmb://', $mode, false, $context); $fh = fopen('nativesmb://', $mode, false, $context);
stream_wrapper_unregister('nativesmb'); stream_wrapper_unregister('nativesmb');
return $fh; return $fh;

View file

@ -9,7 +9,8 @@ namespace Icewind\SMB\Native;
use Icewind\SMB\AbstractServer; use Icewind\SMB\AbstractServer;
use Icewind\SMB\IAuth; use Icewind\SMB\IAuth;
use Icewind\SMB\System; use Icewind\SMB\IOptions;
use Icewind\SMB\ISystem;
use Icewind\SMB\TimeZoneProvider; use Icewind\SMB\TimeZoneProvider;
class NativeServer extends AbstractServer { class NativeServer extends AbstractServer {
@ -18,19 +19,13 @@ class NativeServer extends AbstractServer {
*/ */
protected $state; protected $state;
/** public function __construct($host, IAuth $auth, ISystem $system, TimeZoneProvider $timeZoneProvider, IOptions $options) {
* @param string $host parent::__construct($host, $auth, $system, $timeZoneProvider, $options);
* @param IAuth $auth
* @param System $system
* @param TimeZoneProvider $timeZoneProvider
*/
public function __construct($host, IAuth $auth, System $system, TimeZoneProvider $timeZoneProvider) {
parent::__construct($host, $auth, $system, $timeZoneProvider);
$this->state = new NativeState(); $this->state = new NativeState();
} }
protected function connect() { protected function connect() {
$this->state->init($this->getAuth()); $this->state->init($this->getAuth(), $this->getOptions());
} }
/** /**
@ -40,7 +35,7 @@ class NativeServer extends AbstractServer {
*/ */
public function listShares() { public function listShares() {
$this->connect(); $this->connect();
$shares = array(); $shares = [];
$dh = $this->state->opendir('smb://' . $this->getHost()); $dh = $this->state->opendir('smb://' . $this->getHost());
while ($share = $this->state->readdir($dh)) { while ($share = $this->state->readdir($dh)) {
if ($share['type'] === 'file share') { if ($share['type'] === 'file share') {
@ -62,10 +57,10 @@ class NativeServer extends AbstractServer {
/** /**
* Check if the smbclient php extension is available * Check if the smbclient php extension is available
* *
* @param System $system * @param ISystem $system
* @return bool * @return bool
*/ */
public static function available(System $system) { public static function available(ISystem $system) {
return function_exists('smbclient_state_new'); return $system->libSmbclientAvailable();
} }
} }

View file

@ -53,7 +53,7 @@ class NativeShare extends AbstractShare {
} }
$this->state = new NativeState(); $this->state = new NativeState();
$this->state->init($this->server->getAuth()); $this->state->init($this->server->getAuth(), $this->server->getOptions());
return $this->state; return $this->state;
} }
@ -87,13 +87,16 @@ class NativeShare extends AbstractShare {
* @throws \Icewind\SMB\Exception\InvalidTypeException * @throws \Icewind\SMB\Exception\InvalidTypeException
*/ */
public function dir($path) { public function dir($path) {
$files = array(); $files = [];
$dh = $this->getState()->opendir($this->buildUrl($path)); $dh = $this->getState()->opendir($this->buildUrl($path));
while ($file = $this->getState()->readdir($dh)) { while ($file = $this->getState()->readdir($dh)) {
$name = $file['name']; $name = $file['name'];
if ($name !== '.' and $name !== '..') { if ($name !== '.' and $name !== '..') {
$files [] = new NativeFileInfo($this, $path . '/' . $name, $name); $fullPath = $path . '/' . $name;
$files [] = new NativeFileInfo($this, $fullPath, $name, function () use ($fullPath) {
return $this->getStat($fullPath);
});
} }
} }
@ -106,10 +109,27 @@ class NativeShare extends AbstractShare {
* @return \Icewind\SMB\IFileInfo * @return \Icewind\SMB\IFileInfo
*/ */
public function stat($path) { public function stat($path) {
return new NativeFileInfo($this, $path, basename($path), $this->getStat($path)); return new NativeFileInfo($this, $path, self::mb_basename($path), $this->getStat($path));
} }
public function getStat($path) { /**
* Multibyte unicode safe version of basename()
*
* @param string $path
* @link http://php.net/manual/en/function.basename.php#121405
* @return string
*/
protected static function mb_basename($path) {
if (preg_match('@^.*[\\\\/]([^\\\\/]+)$@s', $path, $matches)) {
return $matches[1];
} elseif (preg_match('@^([^\\\\/]+)$@s', $path, $matches)) {
return $matches[1];
}
return '';
}
private function getStat($path) {
return $this->getState()->stat($this->buildUrl($path)); return $this->getState()->stat($this->buildUrl($path));
} }
@ -228,7 +248,7 @@ class NativeShare extends AbstractShare {
} }
/** /**
* Open a readable stream top a remote file * Open a readable stream to a remote file
* *
* @param string $source * @param string $source
* @return resource a read only stream with the contents of the remote file * @return resource a read only stream with the contents of the remote file
@ -243,10 +263,11 @@ class NativeShare extends AbstractShare {
} }
/** /**
* Open a readable stream top a remote file * Open a writeable stream to a remote file
* Note: This method will truncate the file to 0bytes first
* *
* @param string $source * @param string $source
* @return resource a read only stream with the contents of the remote file * @return resource a writeable stream
* *
* @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\NotFoundException
* @throws \Icewind\SMB\Exception\InvalidTypeException * @throws \Icewind\SMB\Exception\InvalidTypeException
@ -257,6 +278,21 @@ class NativeShare extends AbstractShare {
return NativeWriteStream::wrap($this->getState(), $handle, 'w', $url); return NativeWriteStream::wrap($this->getState(), $handle, 'w', $url);
} }
/**
* Open a writeable stream and set the cursor to the end of the stream
*
* @param string $source
* @return resource a writeable stream
*
* @throws \Icewind\SMB\Exception\NotFoundException
* @throws \Icewind\SMB\Exception\InvalidTypeException
*/
public function append($source) {
$url = $this->buildUrl($source);
$handle = $this->getState()->open($url, "a");
return NativeWriteStream::wrap($this->getState(), $handle, "a", $url);
}
/** /**
* Get extended attributes for the path * Get extended attributes for the path
* *
@ -269,15 +305,14 @@ class NativeShare extends AbstractShare {
} }
/** /**
* Get extended attributes for the path * Set extended attributes for the given path
* *
* @param string $path * @param string $path
* @param string $attribute attribute to get the info * @param string $attribute attribute to get the info
* @param mixed $value * @param string|int $value
* @return string the attribute value * @return mixed the attribute value
*/ */
public function setAttribute($path, $attribute, $value) { public function setAttribute($path, $attribute, $value) {
if ($attribute === 'system.dos_attr.mode' and is_int($value)) { if ($attribute === 'system.dos_attr.mode' and is_int($value)) {
$value = '0x' . dechex($value); $value = '0x' . dechex($value);
} }
@ -286,6 +321,8 @@ class NativeShare extends AbstractShare {
} }
/** /**
* Set DOS comaptible node mode
*
* @param string $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 * @param int $mode a combination of FileInfo::MODE_READONLY, FileInfo::MODE_ARCHIVE, FileInfo::MODE_SYSTEM and FileInfo::MODE_HIDDEN, FileInfo::NORMAL
* @return mixed * @return mixed
@ -295,16 +332,19 @@ class NativeShare extends AbstractShare {
} }
/** /**
* Start smb notify listener
* Note: This is a blocking call
*
* @param string $path * @param string $path
* @return INotifyHandler * @return INotifyHandler
*/ */
public function notify($path) { public function notify($path) {
// php-smbclient does support notify (https://github.com/eduardok/libsmbclient-php/issues/29) // php-smbclient does not support notify (https://github.com/eduardok/libsmbclient-php/issues/29)
// so we use the smbclient based backend for this // so we use the smbclient based backend for this
if (!Server::available($this->server->getSystem())) { if (!Server::available($this->server->getSystem())) {
throw new DependencyException('smbclient not found in path for notify command'); throw new DependencyException('smbclient not found in path for notify command');
} }
$share = new Share($this->server, $this->getName()); $share = new Share($this->server, $this->getName(), $this->server->getSystem());
return $share->notify($path); return $share->notify($path);
} }

View file

@ -21,6 +21,7 @@ use Icewind\SMB\Exception\NotFoundException;
use Icewind\SMB\Exception\OutOfSpaceException; use Icewind\SMB\Exception\OutOfSpaceException;
use Icewind\SMB\Exception\TimedOutException; use Icewind\SMB\Exception\TimedOutException;
use Icewind\SMB\IAuth; use Icewind\SMB\IAuth;
use Icewind\SMB\IOptions;
/** /**
* Low level wrapper for libsmbclient-php with error handling * Low level wrapper for libsmbclient-php with error handling
@ -76,14 +77,16 @@ class NativeState {
/** /**
* @param IAuth $auth * @param IAuth $auth
* @param IOptions $options
* @return bool * @return bool
*/ */
public function init(IAuth $auth) { public function init(IAuth $auth, IOptions $options) {
if ($this->connected) { if ($this->connected) {
return true; return true;
} }
$this->state = smbclient_state_new(); $this->state = smbclient_state_new();
smbclient_option_set($this->state, SMBCLIENT_OPT_AUTO_ANONYMOUS_LOGIN, false); smbclient_option_set($this->state, SMBCLIENT_OPT_AUTO_ANONYMOUS_LOGIN, false);
smbclient_option_set($this->state, SMBCLIENT_OPT_TIMEOUT, $options->getTimeout() * 1000);
$auth->setExtraSmbClientOptions($this->state); $auth->setExtraSmbClientOptions($this->state);
$result = @smbclient_state_init($this->state, $auth->getWorkgroup(), $auth->getUsername(), $auth->getPassword()); $result = @smbclient_state_init($this->state, $auth->getWorkgroup(), $auth->getUsername(), $auth->getPassword());

View file

@ -48,13 +48,13 @@ class NativeStream implements File {
*/ */
public static function wrap($state, $smbStream, $mode, $url) { public static function wrap($state, $smbStream, $mode, $url) {
stream_wrapper_register('nativesmb', NativeStream::class); stream_wrapper_register('nativesmb', NativeStream::class);
$context = stream_context_create(array( $context = stream_context_create([
'nativesmb' => array( 'nativesmb' => [
'state' => $state, 'state' => $state,
'handle' => $smbStream, 'handle' => $smbStream,
'url' => $url 'url' => $url
) ]
)); ]);
$fh = fopen('nativesmb://', $mode, false, $context); $fh = fopen('nativesmb://', $mode, false, $context);
stream_wrapper_unregister('nativesmb'); stream_wrapper_unregister('nativesmb');
return $fh; return $fh;

View file

@ -25,7 +25,6 @@ class NativeWriteStream extends NativeStream {
$this->writeBuffer = fopen('php://memory', 'r+'); $this->writeBuffer = fopen('php://memory', 'r+');
return parent::stream_open($path, $mode, $options, $opened_path); return parent::stream_open($path, $mode, $options, $opened_path);
} }
/** /**
@ -39,13 +38,13 @@ class NativeWriteStream extends NativeStream {
*/ */
public static function wrap($state, $smbStream, $mode, $url) { public static function wrap($state, $smbStream, $mode, $url) {
stream_wrapper_register('nativesmb', NativeWriteStream::class); stream_wrapper_register('nativesmb', NativeWriteStream::class);
$context = stream_context_create(array( $context = stream_context_create([
'nativesmb' => array( 'nativesmb' => [
'state' => $state, 'state' => $state,
'handle' => $smbStream, 'handle' => $smbStream,
'url' => $url 'url' => $url
) ]
)); ]);
$fh = fopen('nativesmb://', $mode, false, $context); $fh = fopen('nativesmb://', $mode, false, $context);
stream_wrapper_unregister('nativesmb'); stream_wrapper_unregister('nativesmb');
return $fh; return $fh;

View file

@ -0,0 +1,35 @@
<?php
/**
* @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace Icewind\SMB;
class Options implements IOptions {
/** @var int */
private $timeout = 20;
public function getTimeout() {
return $this->timeout;
}
public function setTimeout($timeout) {
$this->timeout = $timeout;
}
}

View file

@ -21,7 +21,6 @@
namespace Icewind\SMB; namespace Icewind\SMB;
use Icewind\SMB\Exception\DependencyException; use Icewind\SMB\Exception\DependencyException;
use Icewind\SMB\Native\NativeServer; use Icewind\SMB\Native\NativeServer;
use Icewind\SMB\Wrapped\Server; use Icewind\SMB\Wrapped\Server;
@ -32,8 +31,41 @@ class ServerFactory {
Server::class Server::class
]; ];
/** @var System|null */ /** @var System */
private $system = null; private $system;
/** @var IOptions */
private $options;
/** @var ITimeZoneProvider */
private $timeZoneProvider;
/**
* ServerFactory constructor.
*
* @param IOptions|null $options
* @param ISystem|null $system
* @param ITimeZoneProvider|null $timeZoneProvider
*/
public function __construct(
IOptions $options = null,
ISystem $system = null,
ITimeZoneProvider $timeZoneProvider = null
) {
if (is_null($options)) {
$options = new Options();
}
if (is_null($system)) {
$system = new System();
}
if (is_null($timeZoneProvider)) {
$timeZoneProvider = new TimeZoneProvider($system);
}
$this->options = $options;
$this->system = $system;
$this->timeZoneProvider = $timeZoneProvider;
}
/** /**
* @param $host * @param $host
@ -43,22 +75,11 @@ class ServerFactory {
*/ */
public function createServer($host, IAuth $credentials) { public function createServer($host, IAuth $credentials) {
foreach (self::BACKENDS as $backend) { foreach (self::BACKENDS as $backend) {
if (call_user_func("$backend::available", $this->getSystem())) { if (call_user_func("$backend::available", $this->system)) {
return new $backend($host, $credentials, $this->getSystem(), new TimeZoneProvider($host, $this->getSystem())); return new $backend($host, $credentials, $this->system, $this->timeZoneProvider, $this->options);
} }
} }
throw new DependencyException('No valid backend available, ensure smbclient is in the path or php-smbclient is installed'); throw new DependencyException('No valid backend available, ensure smbclient is in the path or php-smbclient is installed');
} }
/**
* @return System
*/
private function getSystem() {
if (is_null($this->system)) {
$this->system = new System();
}
return $this->system;
}
} }

View file

@ -9,14 +9,18 @@ namespace Icewind\SMB;
use Icewind\SMB\Exception\Exception; use Icewind\SMB\Exception\Exception;
class System { class System implements ISystem {
private $smbclient; /** @var (string|bool)[] */
private $paths = [];
private $net; /**
* Get the path to a file descriptor of the current process
private $stdbuf; *
* @param int $num the file descriptor id
public static function getFD($num) { * @return string
* @throws Exception
*/
public function getFD($num) {
$folders = [ $folders = [
'/proc/self/fd', '/proc/self/fd',
'/dev/fd' '/dev/fd'
@ -30,26 +34,32 @@ class System {
} }
public function getSmbclientPath() { public function getSmbclientPath() {
if (!$this->smbclient) { return $this->getBinaryPath('smbclient');
$this->smbclient = trim(`which smbclient`);
}
return $this->smbclient;
} }
public function getNetPath() { public function getNetPath() {
if (!$this->net) { return $this->getBinaryPath('net');
$this->net = trim(`which net`);
}
return $this->net;
} }
public function hasStdBuf() { public function getStdBufPath() {
if (!$this->stdbuf) { return $this->getBinaryPath('stdbuf');
}
public function getDatePath() {
return $this->getBinaryPath('date');
}
public function libSmbclientAvailable() {
return function_exists('smbclient_state_new');
}
protected function getBinaryPath($binary) {
if (!isset($this->paths[$binary])) {
$result = null; $result = null;
$output = array(); $output = [];
exec('which stdbuf 2>&1', $output, $result); exec("which $binary 2>&1", $output, $result);
$this->stdbuf = $result === 0; $this->paths[$binary] = $result === 0 ? trim(implode('', $output)) : false;
} }
return $this->stdbuf; return $this->paths[$binary];
} }
} }

View file

@ -7,48 +7,48 @@
namespace Icewind\SMB; namespace Icewind\SMB;
class TimeZoneProvider { class TimeZoneProvider implements ITimeZoneProvider {
/** /**
* @var string * @var string[]
*/ */
private $host; private $timeZones = [];
/** /**
* @var string * @var ISystem
*/
private $timeZone;
/**
* @var System
*/ */
private $system; private $system;
/** /**
* @param string $host * @param ISystem $system
* @param System $system
*/ */
public function __construct($host, System $system) { public function __construct(ISystem $system) {
$this->host = $host;
$this->system = $system; $this->system = $system;
} }
public function get() { public function get($host) {
if (!$this->timeZone) { if (!isset($this->timeZones[$host])) {
$timeZone = null;
$net = $this->system->getNetPath(); $net = $this->system->getNetPath();
// for local domain names we can assume same timezone // for local domain names we can assume same timezone
if ($net && strpos($this->host, '.') !== false) { if ($net && $host && strpos($host, '.') !== false) {
$command = sprintf('%s time zone -S %s', $command = sprintf(
'%s time zone -S %s',
$net, $net,
escapeshellarg($this->host) escapeshellarg($host)
); );
$this->timeZone = exec($command); $timeZone = exec($command);
} }
if ($this->timeZone) { if (!$timeZone) {
// fallback to server timezone $date = $this->system->getDatePath();
$this->timeZone = date_default_timezone_get(); if ($date) {
$timeZone = exec($date . " +%z");
} else {
$timeZone = date_default_timezone_get();
}
} }
$this->timeZones[$host] = $timeZone;
} }
return $this->timeZone; return $this->timeZones[$host];
} }
} }

View file

@ -20,7 +20,7 @@ class Connection extends RawConnection {
/** @var Parser */ /** @var Parser */
private $parser; private $parser;
public function __construct($command, Parser $parser, $env = array()) { public function __construct($command, Parser $parser, $env = []) {
parent::__construct($command, $env); parent::__construct($command, $env);
$this->parser = $parser; $this->parser = $parser;
} }
@ -65,7 +65,7 @@ class Connection extends RawConnection {
$promptLine = $this->readLine(); //first line is prompt $promptLine = $this->readLine(); //first line is prompt
$this->parser->checkConnectionError($promptLine); $this->parser->checkConnectionError($promptLine);
$output = array(); $output = [];
$line = $this->readLine(); $line = $this->readLine();
if ($line === false) { if ($line === false) {
$this->unknownError($promptLine); $this->unknownError($promptLine);

View file

@ -8,7 +8,6 @@
namespace Icewind\SMB\Wrapped; namespace Icewind\SMB\Wrapped;
use Icewind\SMB\Change; use Icewind\SMB\Change;
use Icewind\SMB\Exception\Exception; use Icewind\SMB\Exception\Exception;
use Icewind\SMB\Exception\RevisionMismatchException; use Icewind\SMB\Exception\RevisionMismatchException;

View file

@ -19,15 +19,19 @@ use Icewind\SMB\Exception\InvalidTypeException;
use Icewind\SMB\Exception\NoLoginServerException; use Icewind\SMB\Exception\NoLoginServerException;
use Icewind\SMB\Exception\NotEmptyException; use Icewind\SMB\Exception\NotEmptyException;
use Icewind\SMB\Exception\NotFoundException; use Icewind\SMB\Exception\NotFoundException;
use Icewind\SMB\TimeZoneProvider;
class Parser { class Parser {
const MSG_NOT_FOUND = 'Error opening local file '; const MSG_NOT_FOUND = 'Error opening local file ';
/** /**
* @var \Icewind\SMB\TimeZoneProvider * @var string
*/ */
protected $timeZoneProvider; protected $timeZone;
/**
* @var string
*/
private $host;
// todo replace with static once <5.6 support is dropped // todo replace with static once <5.6 support is dropped
// see error.h // see error.h
@ -55,10 +59,10 @@ class Parser {
]; ];
/** /**
* @param TimeZoneProvider $timeZoneProvider * @param string $timeZone
*/ */
public function __construct(TimeZoneProvider $timeZoneProvider) { public function __construct($timeZone) {
$this->timeZoneProvider = $timeZoneProvider; $this->timeZone = $timeZone;
} }
private function getErrorCode($line) { private function getErrorCode($line) {
@ -135,7 +139,10 @@ class Parser {
$name = isset($words[0]) ? $words[0] : ''; $name = isset($words[0]) ? $words[0] : '';
$value = isset($words[1]) ? $words[1] : ''; $value = isset($words[1]) ? $words[1] : '';
$value = trim($value); $value = trim($value);
$data[$name] = $value;
if (!isset($data[$name])) {
$data[$name] = $value;
}
} }
return [ return [
'mtime' => strtotime($data['write_time']), 'mtime' => strtotime($data['write_time']),
@ -149,13 +156,13 @@ class Parser {
array_pop($output); array_pop($output);
$regex = '/^\s*(.*?)\s\s\s\s+(?:([NDHARS]*)\s+)?([0-9]+)\s+(.*)$/'; $regex = '/^\s*(.*?)\s\s\s\s+(?:([NDHARS]*)\s+)?([0-9]+)\s+(.*)$/';
//2 spaces, filename, optional type, size, date //2 spaces, filename, optional type, size, date
$content = array(); $content = [];
foreach ($output as $line) { foreach ($output as $line) {
if (preg_match($regex, $line, $matches)) { if (preg_match($regex, $line, $matches)) {
list(, $name, $mode, $size, $time) = $matches; list(, $name, $mode, $size, $time) = $matches;
if ($name !== '.' and $name !== '..') { if ($name !== '.' and $name !== '..') {
$mode = $this->parseMode($mode); $mode = $this->parseMode($mode);
$time = strtotime($time . ' ' . $this->timeZoneProvider->get()); $time = strtotime($time . ' ' . $this->timeZone);
$content[] = new FileInfo($basePath . '/' . $name, $name, $size, $time, $mode); $content[] = new FileInfo($basePath . '/' . $name, $name, $size, $time, $mode);
} }
} }
@ -164,14 +171,14 @@ class Parser {
} }
public function parseListShares($output) { public function parseListShares($output) {
$shareNames = array(); $shareNames = [];
foreach ($output as $line) { foreach ($output as $line) {
if (strpos($line, '|')) { if (strpos($line, '|')) {
list($type, $name, $description) = explode('|', $line); list($type, $name, $description) = explode('|', $line);
if (strtolower($type) === 'disk') { if (strtolower($type) === 'disk') {
$shareNames[$name] = $description; $shareNames[$name] = $description;
} }
} else if (strpos($line, 'Disk')) { } elseif (strpos($line, 'Disk')) {
// new output format // new output format
list($name, $description) = explode('Disk', $line); list($name, $description) = explode('Disk', $line);
$shareNames[trim($name)] = trim($description); $shareNames[trim($name)] = trim($description);

View file

@ -13,22 +13,22 @@ use Icewind\SMB\Exception\ConnectException;
use Icewind\SMB\Exception\ConnectionException; use Icewind\SMB\Exception\ConnectionException;
use Icewind\SMB\Exception\InvalidHostException; use Icewind\SMB\Exception\InvalidHostException;
use Icewind\SMB\IShare; use Icewind\SMB\IShare;
use Icewind\SMB\System; use Icewind\SMB\ISystem;
class Server extends AbstractServer { class Server extends AbstractServer {
/** /**
* Check if the smbclient php extension is available * Check if the smbclient php extension is available
* *
* @param System $system * @param ISystem $system
* @return bool * @return bool
*/ */
public static function available(System $system) { public static function available(ISystem $system) {
return $system->getSmbclientPath(); return $system->getSmbclientPath();
} }
private function getAuthFileArgument() { private function getAuthFileArgument() {
if ($this->getAuth()->getUsername()) { if ($this->getAuth()->getUsername()) {
return '--authentication-file=' . System::getFD(3); return '--authentication-file=' . $this->system->getFD(3);
} else { } else {
return ''; return '';
} }
@ -42,7 +42,8 @@ class Server extends AbstractServer {
* @throws ConnectException * @throws ConnectException
*/ */
public function listShares() { public function listShares() {
$command = sprintf('%s %s %s -L %s', $command = sprintf(
'%s %s %s -L %s',
$this->system->getSmbclientPath(), $this->system->getSmbclientPath(),
$this->getAuthFileArgument(), $this->getAuthFileArgument(),
$this->getAuth()->getExtraCommandLineArguments(), $this->getAuth()->getExtraCommandLineArguments(),
@ -73,7 +74,7 @@ class Server extends AbstractServer {
$shareNames = $parser->parseListShares($output); $shareNames = $parser->parseListShares($output);
$shares = array(); $shares = [];
foreach ($shareNames as $name => $description) { foreach ($shareNames as $name => $description) {
$shares[] = $this->getShare($name); $shares[] = $this->getShare($name);
} }

View file

@ -13,12 +13,14 @@ use Icewind\SMB\Exception\DependencyException;
use Icewind\SMB\Exception\FileInUseException; use Icewind\SMB\Exception\FileInUseException;
use Icewind\SMB\Exception\InvalidTypeException; use Icewind\SMB\Exception\InvalidTypeException;
use Icewind\SMB\Exception\NotFoundException; use Icewind\SMB\Exception\NotFoundException;
use Icewind\SMB\Exception\InvalidRequestException;
use Icewind\SMB\IFileInfo; use Icewind\SMB\IFileInfo;
use Icewind\SMB\INotifyHandler; use Icewind\SMB\INotifyHandler;
use Icewind\SMB\IServer; use Icewind\SMB\IServer;
use Icewind\SMB\System; use Icewind\SMB\ISystem;
use Icewind\SMB\TimeZoneProvider;
use Icewind\Streams\CallbackWrapper; use Icewind\Streams\CallbackWrapper;
use Icewind\SMB\Native\NativeShare;
use Icewind\SMB\Native\NativeServer;
class Share extends AbstractShare { class Share extends AbstractShare {
/** /**
@ -42,7 +44,7 @@ class Share extends AbstractShare {
protected $parser; protected $parser;
/** /**
* @var System * @var ISystem
*/ */
private $system; private $system;
@ -56,28 +58,30 @@ class Share extends AbstractShare {
/** /**
* @param IServer $server * @param IServer $server
* @param string $name * @param string $name
* @param System $system * @param ISystem $system
*/ */
public function __construct(IServer $server, $name, System $system = null) { public function __construct(IServer $server, $name, ISystem $system) {
parent::__construct(); parent::__construct();
$this->server = $server; $this->server = $server;
$this->name = $name; $this->name = $name;
$this->system = (!is_null($system)) ? $system : new System(); $this->system = $system;
$this->parser = new Parser(new TimeZoneProvider($this->server->getHost(), $this->system)); $this->parser = new Parser($server->getTimeZone());
} }
private function getAuthFileArgument() { private function getAuthFileArgument() {
if ($this->server->getAuth()->getUsername()) { if ($this->server->getAuth()->getUsername()) {
return '--authentication-file=' . System::getFD(3); return '--authentication-file=' . $this->system->getFD(3);
} else { } else {
return ''; return '';
} }
} }
protected function getConnection() { protected function getConnection() {
$command = sprintf('%s%s %s %s %s', $command = sprintf(
$this->system->hasStdBuf() ? 'stdbuf -o0 ' : '', '%s%s -t %s %s %s %s',
$this->system->getStdBufPath() ? $this->system->getStdBufPath() . ' -o0 ' : '',
$this->system->getSmbclientPath(), $this->system->getSmbclientPath(),
$this->server->getOptions()->getTimeout(),
$this->getAuthFileArgument(), $this->getAuthFileArgument(),
$this->server->getAuth()->getExtraCommandLineArguments(), $this->server->getAuth()->getExtraCommandLineArguments(),
escapeshellarg('//' . $this->server->getHost() . '/' . $this->name) escapeshellarg('//' . $this->server->getHost() . '/' . $this->name)
@ -159,13 +163,14 @@ class Share extends AbstractShare {
if ($path !== "" && $path !== "/") { if ($path !== "" && $path !== "/") {
$parent = dirname($path); $parent = dirname($path);
$dir = $this->dir($parent); $dir = $this->dir($parent);
$file = array_values(array_filter($dir, function(IFileInfo $info) use ($path) { $file = array_values(array_filter($dir, function (IFileInfo $info) use ($path) {
return $info->getPath() === $path; return $info->getPath() === $path;
})); }));
if ($file) { if ($file) {
return $file[0]; return $file[0];
} }
} }
$escapedPath = $this->escapePath($path); $escapedPath = $this->escapePath($path);
$output = $this->execute('allinfo ' . $escapedPath); $output = $this->execute('allinfo ' . $escapedPath);
// Windows and non Windows Fileserver may respond different // Windows and non Windows Fileserver may respond different
@ -307,7 +312,7 @@ class Share extends AbstractShare {
// since we can't re-use the same file descriptor over multiple calls // since we can't re-use the same file descriptor over multiple calls
$connection = $this->getConnection(); $connection = $this->getConnection();
$connection->write('get ' . $source . ' ' . System::getFD(5)); $connection->write('get ' . $source . ' ' . $this->system->getFD(5));
$connection->write('exit'); $connection->write('exit');
$fh = $connection->getFileOutputStream(); $fh = $connection->getFileOutputStream();
stream_context_set_option($fh, 'file', 'connection', $connection); stream_context_set_option($fh, 'file', 'connection', $connection);
@ -330,7 +335,7 @@ class Share extends AbstractShare {
$connection = $this->getConnection(); $connection = $this->getConnection();
$fh = $connection->getFileInputStream(); $fh = $connection->getFileInputStream();
$connection->write('put ' . System::getFD(4) . ' ' . $target); $connection->write('put ' . $this->system->getFD(4) . ' ' . $target);
$connection->write('exit'); $connection->write('exit');
// use a close callback to ensure the upload is finished before continuing // use a close callback to ensure the upload is finished before continuing
@ -340,6 +345,18 @@ class Share extends AbstractShare {
}); });
} }
/**
* Append to stream
* Note: smbclient does not support this (Use php-libsmbclient)
*
* @param string $target
*
* @throws \Icewind\SMB\Exception\DependencyException
*/
public function append($target) {
throw new DependencyException('php-libsmbclient is required for append');
}
/** /**
* @param string $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 * @param int $mode a combination of FileInfo::MODE_READONLY, FileInfo::MODE_ARCHIVE, FileInfo::MODE_SYSTEM and FileInfo::MODE_HIDDEN, FileInfo::NORMAL
@ -376,7 +393,7 @@ class Share extends AbstractShare {
* @throws DependencyException * @throws DependencyException
*/ */
public function notify($path) { public function notify($path) {
if (!$this->system->hasStdBuf()) { //stdbuf is required to disable smbclient's output buffering if (!$this->system->getStdBufPath()) { //stdbuf is required to disable smbclient's output buffering
throw new DependencyException('stdbuf is required for usage of the notify command'); throw new DependencyException('stdbuf is required for usage of the notify command');
} }
$connection = $this->getConnection(); // use a fresh connection since the notify command blocks the process $connection = $this->getConnection(); // use a fresh connection since the notify command blocks the process