mirror of
https://codeberg.org/icewind/SMB.git
synced 2026-06-03 09:14:06 +02:00
fix support for smbclient compiled without readline support
This commit is contained in:
parent
9f6b8f1c3f
commit
a8dc9ca75b
6 changed files with 60 additions and 43 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -109,13 +110,30 @@ class RawConnection {
|
|||
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
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue