Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/server.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files_external/3rdparty/icewind/smb/src')
-rw-r--r--apps/files_external/3rdparty/icewind/smb/src/IShare.php2
-rw-r--r--apps/files_external/3rdparty/icewind/smb/src/KerberosApacheAuth.php136
-rw-r--r--apps/files_external/3rdparty/icewind/smb/src/Native/NativeFileInfo.php2
-rw-r--r--apps/files_external/3rdparty/icewind/smb/src/Native/NativeShare.php12
-rw-r--r--apps/files_external/3rdparty/icewind/smb/src/Native/NativeState.php13
-rw-r--r--apps/files_external/3rdparty/icewind/smb/src/System.php2
-rw-r--r--apps/files_external/3rdparty/icewind/smb/src/Wrapped/Connection.php43
-rw-r--r--apps/files_external/3rdparty/icewind/smb/src/Wrapped/NotifyHandler.php14
-rw-r--r--apps/files_external/3rdparty/icewind/smb/src/Wrapped/RawConnection.php34
-rw-r--r--apps/files_external/3rdparty/icewind/smb/src/Wrapped/Share.php14
10 files changed, 219 insertions, 53 deletions
diff --git a/apps/files_external/3rdparty/icewind/smb/src/IShare.php b/apps/files_external/3rdparty/icewind/smb/src/IShare.php
index 6ac6e0d2d15..40213b93a99 100644
--- a/apps/files_external/3rdparty/icewind/smb/src/IShare.php
+++ b/apps/files_external/3rdparty/icewind/smb/src/IShare.php
@@ -45,7 +45,7 @@ interface IShare {
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
* @return resource a read only stream with the contents of the remote file
diff --git a/apps/files_external/3rdparty/icewind/smb/src/KerberosApacheAuth.php b/apps/files_external/3rdparty/icewind/smb/src/KerberosApacheAuth.php
new file mode 100644
index 00000000000..c49918be114
--- /dev/null
+++ b/apps/files_external/3rdparty/icewind/smb/src/KerberosApacheAuth.php
@@ -0,0 +1,136 @@
+<?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 = "";
+
+ /** @var bool */
+ private $init = false;
+
+ /** @var string|false */
+ private $ticketName;
+
+ public function __construct() {
+ $this->ticketName = getenv("KRB5CCNAME");
+ }
+
+
+ /**
+ * Copy the ticket to a temporary location and use that ticket for authentication
+ *
+ * @return void
+ */
+ public function copyTicket(): void {
+ if (!$this->checkTicket()) {
+ return;
+ }
+ $krb5 = new \KRB5CCache();
+ $krb5->open($this->ticketName);
+ $tmpFilename = tempnam("/tmp", "krb5cc_php_");
+ $tmpCacheFile = "FILE:" . $tmpFilename;
+ $krb5->save($tmpCacheFile);
+ $this->ticketPath = $tmpFilename;
+ $this->ticketName = $tmpCacheFile;
+ }
+
+ /**
+ * Pass the ticket to smbclient by memory instead of path
+ *
+ * @return void
+ */
+ public function passTicketFromMemory(): void {
+ if (!$this->checkTicket()) {
+ return;
+ }
+ $krb5 = new \KRB5CCache();
+ $krb5->open($this->ticketName);
+ $this->ticketName = (string)$krb5->getName();
+ }
+
+ /**
+ * Check if a valid kerberos ticket is present
+ *
+ * @return bool
+ * @psalm-assert-if-true string $this->ticketName
+ */
+ public function checkTicket(): bool {
+ //read apache kerberos ticket cache
+ if (!$this->ticketName) {
+ return false;
+ }
+
+ $krb5 = new \KRB5CCache();
+ $krb5->open($this->ticketName);
+ /** @psalm-suppress MixedArgument */
+ return count($krb5->getEntries()) > 0;
+ }
+
+ 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
+ if (!$this->checkTicket()) {
+ throw new Exception('No kerberos ticket cache environment variable (KRB5CCNAME) found.');
+ }
+
+ // note that even if the ticketname is the value we got from `getenv("KRB5CCNAME")` we still need to set the env variable ourselves
+ // this is because `getenv` also reads the variables passed from the SAPI (apache-php) and we need to set the variable in the OS's env
+ putenv("KRB5CCNAME=" . $this->ticketName);
+ }
+
+ public function getExtraCommandLineArguments(): string {
+ $this->init();
+ return parent::getExtraCommandLineArguments();
+ }
+
+ public function setExtraSmbClientOptions($smbClientState): void {
+ $this->init();
+ try {
+ parent::setExtraSmbClientOptions($smbClientState);
+ } catch (Exception $e) {
+ // suppress
+ }
+ }
+
+ public function __destruct() {
+ if (!empty($this->ticketPath) && file_exists($this->ticketPath) && is_file($this->ticketPath)) {
+ unlink($this->ticketPath);
+ }
+ }
+}
diff --git a/apps/files_external/3rdparty/icewind/smb/src/Native/NativeFileInfo.php b/apps/files_external/3rdparty/icewind/smb/src/Native/NativeFileInfo.php
index 539bb728426..85fb0274ac1 100644
--- a/apps/files_external/3rdparty/icewind/smb/src/Native/NativeFileInfo.php
+++ b/apps/files_external/3rdparty/icewind/smb/src/Native/NativeFileInfo.php
@@ -99,7 +99,7 @@ class NativeFileInfo implements IFileInfo {
public function isDirectory(): bool {
$mode = $this->getMode();
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 {
return (bool)($mode & IFileInfo::MODE_DIRECTORY);
}
diff --git a/apps/files_external/3rdparty/icewind/smb/src/Native/NativeShare.php b/apps/files_external/3rdparty/icewind/smb/src/Native/NativeShare.php
index 03ec501b830..8c4eab2a60f 100644
--- a/apps/files_external/3rdparty/icewind/smb/src/Native/NativeShare.php
+++ b/apps/files_external/3rdparty/icewind/smb/src/Native/NativeShare.php
@@ -267,14 +267,14 @@ class NativeShare extends AbstractShare {
* Open a writeable stream to a remote file
* Note: This method will truncate the file to 0bytes first
*
- * @param string $source
+ * @param string $target
* @return resource a writeable stream
*
* @throws NotFoundException
* @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);
return NativeWriteStream::wrap($this->getState(), $handle, 'w', $url);
}
@@ -282,14 +282,14 @@ class NativeShare extends AbstractShare {
/**
* 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
*
* @throws NotFoundException
* @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+");
return NativeWriteStream::wrap($this->getState(), $handle, "a", $url);
}
diff --git a/apps/files_external/3rdparty/icewind/smb/src/Native/NativeState.php b/apps/files_external/3rdparty/icewind/smb/src/Native/NativeState.php
index e1a13ce3e72..088e6f733d1 100644
--- a/apps/files_external/3rdparty/icewind/smb/src/Native/NativeState.php
+++ b/apps/files_external/3rdparty/icewind/smb/src/Native/NativeState.php
@@ -38,6 +38,14 @@ class NativeState {
/** @var bool */
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
const EXCEPTION_MAP = [
1 => ForbiddenException::class,
@@ -107,6 +115,11 @@ class NativeState {
}
$auth->setExtraSmbClientOptions($this->state);
+
+ // sync the garbage collection cycle
+ // __deconstruct() of KerberosAuth should not caled too soon
+ $this->auth = $auth;
+
/** @var bool $result */
$result = @smbclient_state_init($this->state, $auth->getWorkgroup(), $auth->getUsername(), $auth->getPassword());
diff --git a/apps/files_external/3rdparty/icewind/smb/src/System.php b/apps/files_external/3rdparty/icewind/smb/src/System.php
index 919907477ab..d3475e7a5cb 100644
--- a/apps/files_external/3rdparty/icewind/smb/src/System.php
+++ b/apps/files_external/3rdparty/icewind/smb/src/System.php
@@ -62,7 +62,7 @@ class System implements ISystem {
$result = null;
$output = [];
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];
}
diff --git a/apps/files_external/3rdparty/icewind/smb/src/Wrapped/Connection.php b/apps/files_external/3rdparty/icewind/smb/src/Wrapped/Connection.php
index 31b72b05d97..cc73ac1ad14 100644
--- a/apps/files_external/3rdparty/icewind/smb/src/Wrapped/Connection.php
+++ b/apps/files_external/3rdparty/icewind/smb/src/Wrapped/Connection.php
@@ -47,7 +47,7 @@ class Connection extends RawConnection {
public function clearTillPrompt(): void {
$this->write('');
do {
- $promptLine = $this->readLine();
+ $promptLine = $this->readTillPrompt();
if ($promptLine === false) {
break;
}
@@ -56,13 +56,12 @@ class Connection extends RawConnection {
if ($this->write('') === false) {
throw new ConnectionRefusedException();
}
- $this->readLine();
+ $this->readTillPrompt();
}
/**
* 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[]
* @throws AuthenticationException
* @throws ConnectException
@@ -71,42 +70,22 @@ class Connection extends RawConnection {
* @throws NoLoginServerException
* @throws AccessDeniedException
*/
- public function read(callable $callback = null): array {
+ public function read(): array {
if (!$this->isValid()) {
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;
}
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;
}
/**
@@ -132,6 +111,6 @@ class Connection extends RawConnection {
// ignore any errors while trying to send the close command, the process might already be dead
@$this->write('close' . PHP_EOL);
}
- parent::close($terminate);
+ $this->close_process($terminate);
}
}
diff --git a/apps/files_external/3rdparty/icewind/smb/src/Wrapped/NotifyHandler.php b/apps/files_external/3rdparty/icewind/smb/src/Wrapped/NotifyHandler.php
index 18451f4daa6..ecb5bb1e3c1 100644
--- a/apps/files_external/3rdparty/icewind/smb/src/Wrapped/NotifyHandler.php
+++ b/apps/files_external/3rdparty/icewind/smb/src/Wrapped/NotifyHandler.php
@@ -65,16 +65,20 @@ class NotifyHandler implements INotifyHandler {
*/
public function listen(callable $callback): void {
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);
$change = $this->parseChangeLine($line);
if ($change) {
$result = $callback($change);
- return $result === false ? false : true;
- } else {
- return true;
+ if ($result === false) {
+ break;
+ }
}
- });
+ };
}
}
diff --git a/apps/files_external/3rdparty/icewind/smb/src/Wrapped/RawConnection.php b/apps/files_external/3rdparty/icewind/smb/src/Wrapped/RawConnection.php
index 26a17cc584b..4aec674c3da 100644
--- a/apps/files_external/3rdparty/icewind/smb/src/Wrapped/RawConnection.php
+++ b/apps/files_external/3rdparty/icewind/smb/src/Wrapped/RawConnection.php
@@ -71,7 +71,8 @@ class RawConnection {
setlocale(LC_ALL, Server::LOCALE);
$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,
'LANG' => Server::LOCALE,
'COLUMNS' => 8192 // prevent smbclient from line-wrapping it's output
@@ -91,7 +92,7 @@ class RawConnection {
public function isValid(): bool {
if (is_resource($this->process)) {
$status = proc_get_status($this->process);
- return (bool)$status['running'];
+ return $status['running'];
} else {
return false;
}
@@ -110,12 +111,29 @@ class RawConnection {
}
/**
+ * 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
*
* @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);
}
/**
@@ -202,6 +220,14 @@ class RawConnection {
* @psalm-assert null $this->process
*/
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)) {
return;
}
diff --git a/apps/files_external/3rdparty/icewind/smb/src/Wrapped/Share.php b/apps/files_external/3rdparty/icewind/smb/src/Wrapped/Share.php
index 68446d380e0..eb68d3800b3 100644
--- a/apps/files_external/3rdparty/icewind/smb/src/Wrapped/Share.php
+++ b/apps/files_external/3rdparty/icewind/smb/src/Wrapped/Share.php
@@ -345,11 +345,17 @@ class Share extends AbstractShare {
// 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
$connection = $this->getConnection();
+ stream_set_blocking($connection->getOutputStream(), false);
$connection->write('get ' . $source . ' ' . $this->system->getFD(5));
$connection->write('exit');
$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;
}
@@ -374,7 +380,9 @@ class Share extends AbstractShare {
// use a close callback to ensure the upload is finished before continuing
// 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
});
if (is_resource($stream)) {
@@ -446,7 +454,7 @@ class Share extends AbstractShare {
* @return string[]
*/
protected function execute(string $command): array {
- $this->connect()->write($command . PHP_EOL);
+ $this->connect()->write($command);
return $this->connect()->read();
}