Signed-off-by: Robin Appelman <robin@icewind.nl>tags/v24.0.0beta1
icewind/smb/Makefile | icewind/smb/Makefile | ||||
icewind/smb/.travis.yml | icewind/smb/.travis.yml | ||||
icewind/smb/.scrutinizer.yml | icewind/smb/.scrutinizer.yml | ||||
icewind/smb/example-apache-kerberos.php | |||||
icewind/smb/codecov.yml | |||||
icewind/streams/tests | icewind/streams/tests | ||||
.github | .github | ||||
.php_cs* | .php_cs* |
}, | }, | ||||
"require": { | "require": { | ||||
"icewind/streams": "0.7.4", | "icewind/streams": "0.7.4", | ||||
"icewind/smb": "3.4.1" | |||||
"icewind/smb": "3.5.1" | |||||
} | } | ||||
} | } |
"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": "0ffc772b2aaaaffe52decb8d13361976", | |||||
"content-hash": "ed821b15824934fd2d245faca1f35aad", | |||||
"packages": [ | "packages": [ | ||||
{ | { | ||||
"name": "icewind/smb", | "name": "icewind/smb", | ||||
"version": "v3.4.1", | |||||
"version": "v3.5.1", | |||||
"source": { | "source": { | ||||
"type": "git", | "type": "git", | ||||
"url": "https://github.com/icewind1991/SMB.git", | "url": "https://github.com/icewind1991/SMB.git", | ||||
"reference": "9dba42ab2a3990de29e18cc62b0a8270aceb74e3" | |||||
"reference": "c1ce4fbb2ff1786846d9d0b3850b395ca94cf563" | |||||
}, | }, | ||||
"dist": { | "dist": { | ||||
"type": "zip", | "type": "zip", | ||||
"url": "https://api.github.com/repos/icewind1991/SMB/zipball/9dba42ab2a3990de29e18cc62b0a8270aceb74e3", | |||||
"reference": "9dba42ab2a3990de29e18cc62b0a8270aceb74e3", | |||||
"url": "https://api.github.com/repos/icewind1991/SMB/zipball/c1ce4fbb2ff1786846d9d0b3850b395ca94cf563", | |||||
"reference": "c1ce4fbb2ff1786846d9d0b3850b395ca94cf563", | |||||
"shasum": "" | "shasum": "" | ||||
}, | }, | ||||
"require": { | "require": { | ||||
"description": "php wrapper for smbclient and libsmbclient-php", | "description": "php wrapper for smbclient and libsmbclient-php", | ||||
"support": { | "support": { | ||||
"issues": "https://github.com/icewind1991/SMB/issues", | "issues": "https://github.com/icewind1991/SMB/issues", | ||||
"source": "https://github.com/icewind1991/SMB/tree/v3.4.1" | |||||
"source": "https://github.com/icewind1991/SMB/tree/v3.5.1" | |||||
}, | }, | ||||
"time": "2021-04-19T13:53:08+00:00" | |||||
"time": "2021-11-04T14:28:18+00:00" | |||||
}, | }, | ||||
{ | { | ||||
"name": "icewind/streams", | "name": "icewind/streams", |
*/ | */ | ||||
class ClassLoader | class ClassLoader | ||||
{ | { | ||||
/** @var ?string */ | |||||
private $vendorDir; | private $vendorDir; | ||||
// PSR-4 | // PSR-4 | ||||
/** | |||||
* @var array[] | |||||
* @psalm-var array<string, array<string, int>> | |||||
*/ | |||||
private $prefixLengthsPsr4 = array(); | private $prefixLengthsPsr4 = array(); | ||||
/** | |||||
* @var array[] | |||||
* @psalm-var array<string, array<int, string>> | |||||
*/ | |||||
private $prefixDirsPsr4 = array(); | private $prefixDirsPsr4 = array(); | ||||
/** | |||||
* @var array[] | |||||
* @psalm-var array<string, string> | |||||
*/ | |||||
private $fallbackDirsPsr4 = array(); | private $fallbackDirsPsr4 = array(); | ||||
// PSR-0 | // PSR-0 | ||||
/** | |||||
* @var array[] | |||||
* @psalm-var array<string, array<string, string[]>> | |||||
*/ | |||||
private $prefixesPsr0 = array(); | private $prefixesPsr0 = array(); | ||||
/** | |||||
* @var array[] | |||||
* @psalm-var array<string, string> | |||||
*/ | |||||
private $fallbackDirsPsr0 = array(); | private $fallbackDirsPsr0 = array(); | ||||
/** @var bool */ | |||||
private $useIncludePath = false; | private $useIncludePath = false; | ||||
/** | |||||
* @var string[] | |||||
* @psalm-var array<string, string> | |||||
*/ | |||||
private $classMap = array(); | private $classMap = array(); | ||||
/** @var bool */ | |||||
private $classMapAuthoritative = false; | private $classMapAuthoritative = false; | ||||
/** | |||||
* @var bool[] | |||||
* @psalm-var array<string, bool> | |||||
*/ | |||||
private $missingClasses = array(); | private $missingClasses = array(); | ||||
/** @var ?string */ | |||||
private $apcuPrefix; | private $apcuPrefix; | ||||
/** | |||||
* @var self[] | |||||
*/ | |||||
private static $registeredLoaders = array(); | private static $registeredLoaders = array(); | ||||
/** | |||||
* @param ?string $vendorDir | |||||
*/ | |||||
public function __construct($vendorDir = null) | public function __construct($vendorDir = null) | ||||
{ | { | ||||
$this->vendorDir = $vendorDir; | $this->vendorDir = $vendorDir; | ||||
} | } | ||||
/** | |||||
* @return string[] | |||||
*/ | |||||
public function getPrefixes() | public function getPrefixes() | ||||
{ | { | ||||
if (!empty($this->prefixesPsr0)) { | if (!empty($this->prefixesPsr0)) { | ||||
return array(); | return array(); | ||||
} | } | ||||
/** | |||||
* @return array[] | |||||
* @psalm-return array<string, array<int, string>> | |||||
*/ | |||||
public function getPrefixesPsr4() | public function getPrefixesPsr4() | ||||
{ | { | ||||
return $this->prefixDirsPsr4; | return $this->prefixDirsPsr4; | ||||
} | } | ||||
/** | |||||
* @return array[] | |||||
* @psalm-return array<string, string> | |||||
*/ | |||||
public function getFallbackDirs() | public function getFallbackDirs() | ||||
{ | { | ||||
return $this->fallbackDirsPsr0; | return $this->fallbackDirsPsr0; | ||||
} | } | ||||
/** | |||||
* @return array[] | |||||
* @psalm-return array<string, string> | |||||
*/ | |||||
public function getFallbackDirsPsr4() | public function getFallbackDirsPsr4() | ||||
{ | { | ||||
return $this->fallbackDirsPsr4; | return $this->fallbackDirsPsr4; | ||||
} | } | ||||
/** | |||||
* @return string[] Array of classname => path | |||||
* @psalm-var array<string, string> | |||||
*/ | |||||
public function getClassMap() | public function getClassMap() | ||||
{ | { | ||||
return $this->classMap; | return $this->classMap; | ||||
} | } | ||||
/** | /** | ||||
* @param array $classMap Class to filename map | |||||
* @param string[] $classMap Class to filename map | |||||
* @psalm-param array<string, string> $classMap | |||||
* | |||||
* @return void | |||||
*/ | */ | ||||
public function addClassMap(array $classMap) | public function addClassMap(array $classMap) | ||||
{ | { | ||||
* Registers a set of PSR-0 directories for a given prefix, either | * Registers a set of PSR-0 directories for a given prefix, either | ||||
* appending or prepending to the ones previously set for this prefix. | * 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 | |||||
* @param string $prefix The prefix | |||||
* @param string[]|string $paths The PSR-0 root directories | |||||
* @param bool $prepend Whether to prepend the directories | |||||
* | |||||
* @return void | |||||
*/ | */ | ||||
public function add($prefix, $paths, $prepend = false) | public function add($prefix, $paths, $prepend = false) | ||||
{ | { | ||||
* Registers a set of PSR-4 directories for a given namespace, either | * Registers a set of PSR-4 directories for a given namespace, either | ||||
* appending or prepending to the ones previously set for this namespace. | * 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-4 base directories | |||||
* @param bool $prepend Whether to prepend the directories | |||||
* @param string $prefix The prefix/namespace, with trailing '\\' | |||||
* @param string[]|string $paths The PSR-4 base directories | |||||
* @param bool $prepend Whether to prepend the directories | |||||
* | * | ||||
* @throws \InvalidArgumentException | * @throws \InvalidArgumentException | ||||
* | |||||
* @return void | |||||
*/ | */ | ||||
public function addPsr4($prefix, $paths, $prepend = false) | public function addPsr4($prefix, $paths, $prepend = false) | ||||
{ | { | ||||
* Registers a set of PSR-0 directories for a given prefix, | * Registers a set of PSR-0 directories for a given prefix, | ||||
* replacing any others previously set for this prefix. | * replacing any others previously set for this prefix. | ||||
* | * | ||||
* @param string $prefix The prefix | |||||
* @param array|string $paths The PSR-0 base directories | |||||
* @param string $prefix The prefix | |||||
* @param string[]|string $paths The PSR-0 base directories | |||||
* | |||||
* @return void | |||||
*/ | */ | ||||
public function set($prefix, $paths) | public function set($prefix, $paths) | ||||
{ | { | ||||
* Registers a set of PSR-4 directories for a given namespace, | * Registers a set of PSR-4 directories for a given namespace, | ||||
* replacing any others previously set for this 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 | |||||
* @param string $prefix The prefix/namespace, with trailing '\\' | |||||
* @param string[]|string $paths The PSR-4 base directories | |||||
* | * | ||||
* @throws \InvalidArgumentException | * @throws \InvalidArgumentException | ||||
* | |||||
* @return void | |||||
*/ | */ | ||||
public function setPsr4($prefix, $paths) | public function setPsr4($prefix, $paths) | ||||
{ | { | ||||
* Turns on searching the include path for class files. | * Turns on searching the include path for class files. | ||||
* | * | ||||
* @param bool $useIncludePath | * @param bool $useIncludePath | ||||
* | |||||
* @return void | |||||
*/ | */ | ||||
public function setUseIncludePath($useIncludePath) | public function setUseIncludePath($useIncludePath) | ||||
{ | { | ||||
* that have not been registered with the class map. | * that have not been registered with the class map. | ||||
* | * | ||||
* @param bool $classMapAuthoritative | * @param bool $classMapAuthoritative | ||||
* | |||||
* @return void | |||||
*/ | */ | ||||
public function setClassMapAuthoritative($classMapAuthoritative) | public function setClassMapAuthoritative($classMapAuthoritative) | ||||
{ | { | ||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled. | * APCu prefix to use to cache found/not-found classes, if the extension is enabled. | ||||
* | * | ||||
* @param string|null $apcuPrefix | * @param string|null $apcuPrefix | ||||
* | |||||
* @return void | |||||
*/ | */ | ||||
public function setApcuPrefix($apcuPrefix) | public function setApcuPrefix($apcuPrefix) | ||||
{ | { | ||||
* Registers this instance as an autoloader. | * Registers this instance as an autoloader. | ||||
* | * | ||||
* @param bool $prepend Whether to prepend the autoloader or not | * @param bool $prepend Whether to prepend the autoloader or not | ||||
* | |||||
* @return void | |||||
*/ | */ | ||||
public function register($prepend = false) | public function register($prepend = false) | ||||
{ | { | ||||
/** | /** | ||||
* Unregisters this instance as an autoloader. | * Unregisters this instance as an autoloader. | ||||
* | |||||
* @return void | |||||
*/ | */ | ||||
public function unregister() | public function unregister() | ||||
{ | { | ||||
return self::$registeredLoaders; | return self::$registeredLoaders; | ||||
} | } | ||||
/** | |||||
* @param string $class | |||||
* @param string $ext | |||||
* @return string|false | |||||
*/ | |||||
private function findFileWithExtension($class, $ext) | private function findFileWithExtension($class, $ext) | ||||
{ | { | ||||
// PSR-4 lookup | // PSR-4 lookup | ||||
* Scope isolated include. | * Scope isolated include. | ||||
* | * | ||||
* Prevents access to $this/self from included files. | * Prevents access to $this/self from included files. | ||||
* | |||||
* @param string $file | |||||
* @return void | |||||
* @private | |||||
*/ | */ | ||||
function includeFile($file) | function includeFile($file) | ||||
{ | { |
* | * | ||||
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions | * See also https://getcomposer.org/doc/07-runtime.md#installed-versions | ||||
* | * | ||||
* To require it's presence, you can require `composer-runtime-api ^2.0` | |||||
* To require its presence, you can require `composer-runtime-api ^2.0` | |||||
*/ | */ | ||||
class InstalledVersions | class InstalledVersions | ||||
{ | { | ||||
/** | |||||
* @var mixed[]|null | |||||
* @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null | |||||
*/ | |||||
private static $installed; | private static $installed; | ||||
/** | |||||
* @var bool|null | |||||
*/ | |||||
private static $canGetVendors; | private static $canGetVendors; | ||||
/** | |||||
* @var array[] | |||||
* @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}> | |||||
*/ | |||||
private static $installedByVendor = array(); | private static $installedByVendor = array(); | ||||
/** | /** | ||||
/** | /** | ||||
* @return array | * @return array | ||||
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string} | |||||
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string} | |||||
*/ | */ | ||||
public static function getRootPackage() | public static function getRootPackage() | ||||
{ | { | ||||
* | * | ||||
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. | * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. | ||||
* @return array[] | * @return array[] | ||||
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>} | |||||
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} | |||||
*/ | */ | ||||
public static function getRawData() | public static function getRawData() | ||||
{ | { | ||||
* Returns the raw data of all installed.php which are currently loaded for custom implementations | * Returns the raw data of all installed.php which are currently loaded for custom implementations | ||||
* | * | ||||
* @return array[] | * @return array[] | ||||
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}> | |||||
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}> | |||||
*/ | */ | ||||
public static function getAllRawData() | public static function getAllRawData() | ||||
{ | { | ||||
* @param array[] $data A vendor/composer/installed.php data set | * @param array[] $data A vendor/composer/installed.php data set | ||||
* @return void | * @return void | ||||
* | * | ||||
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>} $data | |||||
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data | |||||
*/ | */ | ||||
public static function reload($data) | public static function reload($data) | ||||
{ | { | ||||
/** | /** | ||||
* @return array[] | * @return array[] | ||||
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}> | |||||
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}> | |||||
*/ | */ | ||||
private static function getInstalled() | private static function getInstalled() | ||||
{ | { |
'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\\ISystem' => $vendorDir . '/icewind/smb/src/ISystem.php', | ||||
'Icewind\\SMB\\ITimeZoneProvider' => $vendorDir . '/icewind/smb/src/ITimeZoneProvider.php', | 'Icewind\\SMB\\ITimeZoneProvider' => $vendorDir . '/icewind/smb/src/ITimeZoneProvider.php', | ||||
'Icewind\\SMB\\KerberosApacheAuth' => $vendorDir . '/icewind/smb/src/KerberosApacheAuth.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', |
'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\\ISystem' => __DIR__ . '/..' . '/icewind/smb/src/ISystem.php', | ||||
'Icewind\\SMB\\ITimeZoneProvider' => __DIR__ . '/..' . '/icewind/smb/src/ITimeZoneProvider.php', | 'Icewind\\SMB\\ITimeZoneProvider' => __DIR__ . '/..' . '/icewind/smb/src/ITimeZoneProvider.php', | ||||
'Icewind\\SMB\\KerberosApacheAuth' => __DIR__ . '/..' . '/icewind/smb/src/KerberosApacheAuth.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', |
"packages": [ | "packages": [ | ||||
{ | { | ||||
"name": "icewind/smb", | "name": "icewind/smb", | ||||
"version": "v3.4.1", | |||||
"version_normalized": "3.4.1.0", | |||||
"version": "v3.5.1", | |||||
"version_normalized": "3.5.1.0", | |||||
"source": { | "source": { | ||||
"type": "git", | "type": "git", | ||||
"url": "https://github.com/icewind1991/SMB.git", | "url": "https://github.com/icewind1991/SMB.git", | ||||
"reference": "9dba42ab2a3990de29e18cc62b0a8270aceb74e3" | |||||
"reference": "c1ce4fbb2ff1786846d9d0b3850b395ca94cf563" | |||||
}, | }, | ||||
"dist": { | "dist": { | ||||
"type": "zip", | "type": "zip", | ||||
"url": "https://api.github.com/repos/icewind1991/SMB/zipball/9dba42ab2a3990de29e18cc62b0a8270aceb74e3", | |||||
"reference": "9dba42ab2a3990de29e18cc62b0a8270aceb74e3", | |||||
"url": "https://api.github.com/repos/icewind1991/SMB/zipball/c1ce4fbb2ff1786846d9d0b3850b395ca94cf563", | |||||
"reference": "c1ce4fbb2ff1786846d9d0b3850b395ca94cf563", | |||||
"shasum": "" | "shasum": "" | ||||
}, | }, | ||||
"require": { | "require": { | ||||
"phpunit/phpunit": "^8.5|^9.3.8", | "phpunit/phpunit": "^8.5|^9.3.8", | ||||
"psalm/phar": "^4.3" | "psalm/phar": "^4.3" | ||||
}, | }, | ||||
"time": "2021-04-19T13:53:08+00:00", | |||||
"time": "2021-11-04T14:28:18+00:00", | |||||
"type": "library", | "type": "library", | ||||
"installation-source": "dist", | "installation-source": "dist", | ||||
"autoload": { | "autoload": { | ||||
"description": "php wrapper for smbclient and libsmbclient-php", | "description": "php wrapper for smbclient and libsmbclient-php", | ||||
"support": { | "support": { | ||||
"issues": "https://github.com/icewind1991/SMB/issues", | "issues": "https://github.com/icewind1991/SMB/issues", | ||||
"source": "https://github.com/icewind1991/SMB/tree/v3.4.1" | |||||
"source": "https://github.com/icewind1991/SMB/tree/v3.5.1" | |||||
}, | }, | ||||
"install-path": "../icewind/smb" | "install-path": "../icewind/smb" | ||||
}, | }, |
'type' => 'library', | 'type' => 'library', | ||||
'install_path' => __DIR__ . '/../', | 'install_path' => __DIR__ . '/../', | ||||
'aliases' => array(), | 'aliases' => array(), | ||||
'reference' => '70483a16a3a232758979bb6fa363629b5a16b6a4', | |||||
'reference' => 'cd72330b8f669e3dc81388be5a92171404f36fec', | |||||
'name' => 'files_external/3rdparty', | 'name' => 'files_external/3rdparty', | ||||
'dev' => true, | 'dev' => true, | ||||
), | ), | ||||
'type' => 'library', | 'type' => 'library', | ||||
'install_path' => __DIR__ . '/../', | 'install_path' => __DIR__ . '/../', | ||||
'aliases' => array(), | 'aliases' => array(), | ||||
'reference' => '70483a16a3a232758979bb6fa363629b5a16b6a4', | |||||
'reference' => 'cd72330b8f669e3dc81388be5a92171404f36fec', | |||||
'dev_requirement' => false, | 'dev_requirement' => false, | ||||
), | ), | ||||
'icewind/smb' => array( | 'icewind/smb' => array( | ||||
'pretty_version' => 'v3.4.1', | |||||
'version' => '3.4.1.0', | |||||
'pretty_version' => 'v3.5.1', | |||||
'version' => '3.5.1.0', | |||||
'type' => 'library', | 'type' => 'library', | ||||
'install_path' => __DIR__ . '/../icewind/smb', | 'install_path' => __DIR__ . '/../icewind/smb', | ||||
'aliases' => array(), | 'aliases' => array(), | ||||
'reference' => '9dba42ab2a3990de29e18cc62b0a8270aceb74e3', | |||||
'reference' => 'c1ce4fbb2ff1786846d9d0b3850b395ca94cf563', | |||||
'dev_requirement' => false, | 'dev_requirement' => false, | ||||
), | ), | ||||
'icewind/streams' => array( | 'icewind/streams' => array( |
### Using kerberos authentication ### | ### Using kerberos authentication ### | ||||
There are two ways of using kerberos to authenticate against the smb server: | |||||
- Using a ticket from the php server | |||||
- Re-using a ticket send by the client | |||||
### Using a server ticket | |||||
Using a server ticket allows the web server to authenticate against the smb server using an existing machine account. | |||||
The ticket needs to be available in the environment of the php process. | |||||
```php | ```php | ||||
$serverFactory = new ServerFactory(); | $serverFactory = new ServerFactory(); | ||||
$auth = new KerberosAuth(); | $auth = new KerberosAuth(); | ||||
$server = $serverFactory->createServer('localhost', $auth); | $server = $serverFactory->createServer('localhost', $auth); | ||||
``` | ``` | ||||
Note that this requires a valid kerberos ticket to already be available for php | |||||
### Re-using a client ticket | |||||
By re-using a client ticket you can create a single sign-on setup where the user authenticates against | |||||
the web service using kerberos. And the web server can forward that ticket to the smb server, allowing it | |||||
to act on the behalf of the user without requiring the user to enter his passord. | |||||
The setup for such a system is fairly involved and requires roughly the following this | |||||
- The web server is authenticated against kerberos with a machine account | |||||
- Delegation is enabled for the web server's machine account | |||||
- Apache is setup to perform kerberos authentication and save the ticket in it's environment | |||||
- Php has the krb5 extension installed | |||||
- The client authenticates using a ticket with forwarding enabled | |||||
```php | |||||
$serverFactory = new ServerFactory(); | |||||
$auth = new KerberosApacheAuth(); | |||||
$server = $serverFactory->createServer('localhost', $auth); | |||||
``` | |||||
### Upload a file ### | ### Upload a file ### | ||||
public function put(string $source, string $target): bool; | public function put(string $source, string $target): bool; | ||||
/** | /** | ||||
* 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 |
<?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; | |||||
use Icewind\SMB\Exception\DependencyException; | |||||
use Icewind\SMB\Exception\Exception; | |||||
/** | |||||
* Use existing kerberos ticket to authenticate and reuse the apache ticket cache (mod_auth_kerb) | |||||
*/ | |||||
class KerberosApacheAuth extends KerberosAuth implements IAuth { | |||||
/** @var string */ | |||||
private $ticketPath = ""; | |||||
// only working with specific library (mod_auth_kerb, krb5, smbclient) versions | |||||
/** @var bool */ | |||||
private $saveTicketInMemory = false; | |||||
/** @var bool */ | |||||
private $init = false; | |||||
/** | |||||
* @param bool $saveTicketInMemory | |||||
*/ | |||||
public function __construct(bool $saveTicketInMemory = false) { | |||||
$this->saveTicketInMemory = $saveTicketInMemory; | |||||
} | |||||
/** | |||||
* Check if a valid kerberos ticket is present | |||||
* | |||||
* @return bool | |||||
*/ | |||||
public function checkTicket(): bool { | |||||
//read apache kerberos ticket cache | |||||
$cacheFile = getenv("KRB5CCNAME"); | |||||
if (!$cacheFile) { | |||||
return false; | |||||
} | |||||
$krb5 = new \KRB5CCache(); | |||||
$krb5->open($cacheFile); | |||||
return (bool)$krb5->isValid(); | |||||
} | |||||
private function init(): void { | |||||
if ($this->init) { | |||||
return; | |||||
} | |||||
$this->init = true; | |||||
// inspired by https://git.typo3.org/TYPO3CMS/Extensions/fal_cifs.git | |||||
if (!extension_loaded("krb5")) { | |||||
// https://pecl.php.net/package/krb5 | |||||
throw new DependencyException('Ensure php-krb5 is installed.'); | |||||
} | |||||
//read apache kerberos ticket cache | |||||
$cacheFile = getenv("KRB5CCNAME"); | |||||
if (!$cacheFile) { | |||||
throw new Exception('No kerberos ticket cache environment variable (KRB5CCNAME) found.'); | |||||
} | |||||
$krb5 = new \KRB5CCache(); | |||||
$krb5->open($cacheFile); | |||||
if (!$krb5->isValid()) { | |||||
throw new Exception('Kerberos ticket cache is not valid.'); | |||||
} | |||||
if ($this->saveTicketInMemory) { | |||||
putenv("KRB5CCNAME=" . (string)$krb5->getName()); | |||||
} else { | |||||
//workaround: smbclient is not working with the original apache ticket cache. | |||||
$tmpFilename = tempnam("/tmp", "krb5cc_php_"); | |||||
$tmpCacheFile = "FILE:" . $tmpFilename; | |||||
$krb5->save($tmpCacheFile); | |||||
$this->ticketPath = $tmpFilename; | |||||
putenv("KRB5CCNAME=" . $tmpCacheFile); | |||||
} | |||||
} | |||||
public function getExtraCommandLineArguments(): string { | |||||
$this->init(); | |||||
return parent::getExtraCommandLineArguments(); | |||||
} | |||||
public function setExtraSmbClientOptions($smbClientState): void { | |||||
$this->init(); | |||||
parent::setExtraSmbClientOptions($smbClientState); | |||||
} | |||||
public function __destruct() { | |||||
if (!empty($this->ticketPath) && file_exists($this->ticketPath) && is_file($this->ticketPath)) { | |||||
unlink($this->ticketPath); | |||||
} | |||||
} | |||||
} |
public function isDirectory(): bool { | public function isDirectory(): bool { | ||||
$mode = $this->getMode(); | $mode = $this->getMode(); | ||||
if ($mode > 0x1000) { | if ($mode > 0x1000) { | ||||
return (bool)($mode & 0x4000); // 0x4000: unix directory flag | |||||
return ($mode & 0x4000 && !($mode & 0x8000)); // 0x4000: unix directory flag shares bits with 0xC000: socket | |||||
} else { | } else { | ||||
return (bool)($mode & IFileInfo::MODE_DIRECTORY); | return (bool)($mode & IFileInfo::MODE_DIRECTORY); | ||||
} | } |
* Open a writeable stream to a remote file | * Open a writeable stream to a remote file | ||||
* Note: This method will truncate the file to 0bytes first | * Note: This method will truncate the file to 0bytes first | ||||
* | * | ||||
* @param string $source | |||||
* @param string $target | |||||
* @return resource a writeable stream | * @return resource a writeable stream | ||||
* | * | ||||
* @throws NotFoundException | * @throws NotFoundException | ||||
* @throws InvalidTypeException | * @throws InvalidTypeException | ||||
*/ | */ | ||||
public function write(string $source) { | |||||
$url = $this->buildUrl($source); | |||||
public function write(string $target) { | |||||
$url = $this->buildUrl($target); | |||||
$handle = $this->getState()->create($url); | $handle = $this->getState()->create($url); | ||||
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 | * Open a writeable stream and set the cursor to the end of the stream | ||||
* | * | ||||
* @param string $source | |||||
* @param string $target | |||||
* @return resource a writeable stream | * @return resource a writeable stream | ||||
* | * | ||||
* @throws NotFoundException | * @throws NotFoundException | ||||
* @throws InvalidTypeException | * @throws InvalidTypeException | ||||
*/ | */ | ||||
public function append(string $source) { | |||||
$url = $this->buildUrl($source); | |||||
public function append(string $target) { | |||||
$url = $this->buildUrl($target); | |||||
$handle = $this->getState()->open($url, "a+"); | $handle = $this->getState()->open($url, "a+"); | ||||
return NativeWriteStream::wrap($this->getState(), $handle, "a", $url); | return NativeWriteStream::wrap($this->getState(), $handle, "a", $url); | ||||
} | } |
/** @var bool */ | /** @var bool */ | ||||
protected $connected = false; | protected $connected = false; | ||||
/** | |||||
* sync the garbage collection cycle | |||||
* __deconstruct() of KerberosAuth should not called too soon | |||||
* | |||||
* @var IAuth|null $auth | |||||
*/ | |||||
protected $auth = null; | |||||
// see error.h | // see error.h | ||||
const EXCEPTION_MAP = [ | const EXCEPTION_MAP = [ | ||||
1 => ForbiddenException::class, | 1 => ForbiddenException::class, | ||||
} | } | ||||
$auth->setExtraSmbClientOptions($this->state); | $auth->setExtraSmbClientOptions($this->state); | ||||
// sync the garbage collection cycle | |||||
// __deconstruct() of KerberosAuth should not caled too soon | |||||
$this->auth = $auth; | |||||
/** @var bool $result */ | /** @var bool $result */ | ||||
$result = @smbclient_state_init($this->state, $auth->getWorkgroup(), $auth->getUsername(), $auth->getPassword()); | $result = @smbclient_state_init($this->state, $auth->getWorkgroup(), $auth->getUsername(), $auth->getPassword()); | ||||
$result = null; | $result = null; | ||||
$output = []; | $output = []; | ||||
exec("which $binary 2>&1", $output, $result); | exec("which $binary 2>&1", $output, $result); | ||||
$this->paths[$binary] = $result === 0 ? trim(implode('', $output)) : null; | |||||
$this->paths[$binary] = $result === 0 && isset($output[0]) ? (string)$output[0] : null; | |||||
} | } | ||||
return $this->paths[$binary]; | return $this->paths[$binary]; | ||||
} | } |
public function clearTillPrompt(): void { | public function clearTillPrompt(): void { | ||||
$this->write(''); | $this->write(''); | ||||
do { | do { | ||||
$promptLine = $this->readLine(); | |||||
$promptLine = $this->readTillPrompt(); | |||||
if ($promptLine === false) { | if ($promptLine === false) { | ||||
break; | break; | ||||
} | } | ||||
if ($this->write('') === false) { | if ($this->write('') === false) { | ||||
throw new ConnectionRefusedException(); | throw new ConnectionRefusedException(); | ||||
} | } | ||||
$this->readLine(); | |||||
$this->readTillPrompt(); | |||||
} | } | ||||
/** | /** | ||||
* get all unprocessed output from smbclient until the next prompt | * get all unprocessed output from smbclient until the next prompt | ||||
* | * | ||||
* @param (callable(string):bool)|null $callback (optional) callback to call for every line read | |||||
* @return string[] | * @return string[] | ||||
* @throws AuthenticationException | * @throws AuthenticationException | ||||
* @throws ConnectException | * @throws ConnectException | ||||
* @throws NoLoginServerException | * @throws NoLoginServerException | ||||
* @throws AccessDeniedException | * @throws AccessDeniedException | ||||
*/ | */ | ||||
public function read(callable $callback = null): array { | |||||
public function read(): array { | |||||
if (!$this->isValid()) { | if (!$this->isValid()) { | ||||
throw new ConnectionException('Connection not valid'); | throw new ConnectionException('Connection not valid'); | ||||
} | } | ||||
$promptLine = $this->readLine(); //first line is prompt | |||||
if ($promptLine === false) { | |||||
$this->unknownError($promptLine); | |||||
} | |||||
$this->parser->checkConnectionError($promptLine); | |||||
$output = []; | |||||
if (!$this->isPrompt($promptLine)) { | |||||
$line = $promptLine; | |||||
} else { | |||||
$line = $this->readLine(); | |||||
} | |||||
if ($line === false) { | |||||
$this->unknownError($promptLine); | |||||
} | |||||
while ($line !== false && !$this->isPrompt($line)) { //next prompt functions as delimiter | |||||
if (is_callable($callback)) { | |||||
$result = $callback($line); | |||||
if ($result === false) { // allow the callback to close the connection for infinite running commands | |||||
$this->close(true); | |||||
break; | |||||
} | |||||
} else { | |||||
$output[] = $line; | |||||
} | |||||
$line = $this->readLine(); | |||||
$output = $this->readTillPrompt(); | |||||
if ($output === false) { | |||||
$this->unknownError(false); | |||||
} | } | ||||
$output = explode("\n", $output); | |||||
// last line contains the prompt | |||||
array_pop($output); | |||||
return $output; | return $output; | ||||
} | } | ||||
private function isPrompt(string $line): bool { | private function isPrompt(string $line): bool { | ||||
return mb_substr($line, 0, self::DELIMITER_LENGTH) === self::DELIMITER; | |||||
return substr($line, 0, self::DELIMITER_LENGTH) === self::DELIMITER; | |||||
} | } | ||||
/** | /** | ||||
// ignore any errors while trying to send the close command, the process might already be dead | // ignore any errors while trying to send the close command, the process might already be dead | ||||
@$this->write('close' . PHP_EOL); | @$this->write('close' . PHP_EOL); | ||||
} | } | ||||
parent::close($terminate); | |||||
$this->close_process($terminate); | |||||
} | } | ||||
} | } |
*/ | */ | ||||
public function listen(callable $callback): void { | public function listen(callable $callback): void { | ||||
if ($this->listening) { | if ($this->listening) { | ||||
$this->connection->read(function (string $line) use ($callback): bool { | |||||
while (true) { | |||||
$line = $this->connection->readLine(); | |||||
if ($line === false) { | |||||
break; | |||||
} | |||||
$this->checkForError($line); | $this->checkForError($line); | ||||
$change = $this->parseChangeLine($line); | $change = $this->parseChangeLine($line); | ||||
if ($change) { | if ($change) { | ||||
$result = $callback($change); | $result = $callback($change); | ||||
return $result === false ? false : true; | |||||
} else { | |||||
return true; | |||||
if ($result === false) { | |||||
break; | |||||
} | |||||
} | } | ||||
}); | |||||
}; | |||||
} | } | ||||
} | } | ||||
setlocale(LC_ALL, Server::LOCALE); | setlocale(LC_ALL, Server::LOCALE); | ||||
$env = array_merge($this->env, [ | $env = array_merge($this->env, [ | ||||
'CLI_FORCE_INTERACTIVE' => 'y', // Needed or the prompt isn't displayed!! | |||||
'CLI_FORCE_INTERACTIVE' => 'y', // Make sure the prompt is displayed | |||||
'CLI_NO_READLINE' => 1, // Not all distros build smbclient with readline, disable it to get consistent behaviour | |||||
'LC_ALL' => Server::LOCALE, | 'LC_ALL' => Server::LOCALE, | ||||
'LANG' => Server::LOCALE, | 'LANG' => Server::LOCALE, | ||||
'COLUMNS' => 8192 // prevent smbclient from line-wrapping it's output | 'COLUMNS' => 8192 // prevent smbclient from line-wrapping it's output | ||||
public function isValid(): bool { | public function isValid(): bool { | ||||
if (is_resource($this->process)) { | if (is_resource($this->process)) { | ||||
$status = proc_get_status($this->process); | $status = proc_get_status($this->process); | ||||
return (bool)$status['running']; | |||||
return $status['running']; | |||||
} else { | } else { | ||||
return false; | return false; | ||||
} | } | ||||
return $result; | return $result; | ||||
} | } | ||||
/** | |||||
* read output till the next prompt | |||||
* | |||||
* @return string|false | |||||
*/ | |||||
public function readTillPrompt() { | |||||
$output = ""; | |||||
do { | |||||
$chunk = $this->readLine('\> '); | |||||
if ($chunk === false) { | |||||
return false; | |||||
} | |||||
$output .= $chunk; | |||||
} while (strlen($chunk) == 4096 && strpos($chunk, "smb:") === false); | |||||
return $output; | |||||
} | |||||
/** | /** | ||||
* read a line of output | * read a line of output | ||||
* | * | ||||
* @return string|false | * @return string|false | ||||
*/ | */ | ||||
public function readLine() { | |||||
return stream_get_line($this->getOutputStream(), 4086, "\n"); | |||||
public function readLine(string $end = "\n") { | |||||
return stream_get_line($this->getOutputStream(), 4096, $end); | |||||
} | } | ||||
/** | /** | ||||
* @psalm-assert null $this->process | * @psalm-assert null $this->process | ||||
*/ | */ | ||||
public function close(bool $terminate = true): void { | public function close(bool $terminate = true): void { | ||||
$this->close_process($terminate); | |||||
} | |||||
/** | |||||
* @param bool $terminate | |||||
* @psalm-assert null $this->process | |||||
*/ | |||||
protected function close_process(bool $terminate = true): void { | |||||
if (!is_resource($this->process)) { | if (!is_resource($this->process)) { | ||||
return; | return; | ||||
} | } |
// since returned stream is closed by the caller we need to create a new instance | // 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 | // since we can't re-use the same file descriptor over multiple calls | ||||
$connection = $this->getConnection(); | $connection = $this->getConnection(); | ||||
stream_set_blocking($connection->getOutputStream(), false); | |||||
$connection->write('get ' . $source . ' ' . $this->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); | |||||
$fh = CallbackWrapper::wrap($fh, function() use ($connection) { | |||||
$connection->write(''); | |||||
}); | |||||
if (!is_resource($fh)) { | |||||
throw new Exception("Failed to wrap file output"); | |||||
} | |||||
return $fh; | return $fh; | ||||
} | } | ||||
// use a close callback to ensure the upload is finished before continuing | // use a close callback to ensure the upload is finished before continuing | ||||
// this also serves as a way to keep the connection in scope | // this also serves as a way to keep the connection in scope | ||||
$stream = CallbackWrapper::wrap($fh, null, null, function () use ($connection) { | |||||
$stream = CallbackWrapper::wrap($fh, function() use ($connection) { | |||||
$connection->write(''); | |||||
}, null, function () use ($connection) { | |||||
$connection->close(false); // dont terminate, give the upload some time | $connection->close(false); // dont terminate, give the upload some time | ||||
}); | }); | ||||
if (is_resource($stream)) { | if (is_resource($stream)) { | ||||
* @return string[] | * @return string[] | ||||
*/ | */ | ||||
protected function execute(string $command): array { | protected function execute(string $command): array { | ||||
$this->connect()->write($command . PHP_EOL); | |||||
$this->connect()->write($command); | |||||
return $this->connect()->read(); | return $this->connect()->read(); | ||||
} | } | ||||