Skip to content
This repository was archived by the owner on May 30, 2025. It is now read-only.

Commit 9132f32

Browse files
authored
Merge pull request #108 from icewind1991/disable-readline
fix support for smbclient compiled without readline support
2 parents 9f6b8f1 + 9db846f commit 9132f32

File tree

8 files changed

+93
-46
lines changed

8 files changed

+93
-46
lines changed

.github/workflows/ci.yaml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,39 @@ jobs:
138138
with:
139139
files: ./coverage.xml
140140

141+
alpine-test:
142+
runs-on: ubuntu-20.04
143+
name: Unit tests (alpine)
144+
145+
services:
146+
samba:
147+
image: "servercontainers/samba"
148+
env:
149+
ACCOUNT_test: test
150+
UID_test: 1000
151+
SAMBA_VOLUME_CONFIG_test: "[test]; path=/tmp; valid users = test; guest ok = no; read only = no; browseable = yes"
152+
ports:
153+
- 139:139
154+
- 445:445
155+
156+
steps:
157+
- uses: actions/checkout@v2
158+
- name: Setup PHP
159+
uses: shivammathur/setup-php@v2
160+
with:
161+
php-version: 8.0
162+
- name: Composer
163+
run: composer install
164+
- name: Pull images
165+
run: |
166+
docker pull icewind1991/smbclient-php-alpine
167+
- name: Config
168+
run: |
169+
echo '{"host": "localhost","user": "test","password": "test","share": "test","root": ""}' > tests/config.json
170+
- name: PHPUnit Tests
171+
run: |
172+
docker run --network "host" --rm -v $PWD:/smb icewind1991/smbclient-php-alpine /smb/vendor/bin/phpunit -c /smb/tests/phpunit.xml /smb/tests
173+
141174
kerberos-sso:
142175
runs-on: ubuntu-20.04
143176
name: Kerberos SSO tests

src/IShare.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public function get(string $source, string $target): bool;
4545
public function put(string $source, string $target): bool;
4646

4747
/**
48-
* Open a readable stream top a remote file
48+
* Open a readable stream to a remote file
4949
*
5050
* @param string $source
5151
* @return resource a read only stream with the contents of the remote file

src/Wrapped/Connection.php

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public function write(string $input) {
4747
public function clearTillPrompt(): void {
4848
$this->write('');
4949
do {
50-
$promptLine = $this->readLine();
50+
$promptLine = $this->readTillPrompt();
5151
if ($promptLine === false) {
5252
break;
5353
}
@@ -56,13 +56,12 @@ public function clearTillPrompt(): void {
5656
if ($this->write('') === false) {
5757
throw new ConnectionRefusedException();
5858
}
59-
$this->readLine();
59+
$this->readTillPrompt();
6060
}
6161

6262
/**
6363
* get all unprocessed output from smbclient until the next prompt
6464
*
65-
* @param (callable(string):bool)|null $callback (optional) callback to call for every line read
6665
* @return string[]
6766
* @throws AuthenticationException
6867
* @throws ConnectException
@@ -71,42 +70,22 @@ public function clearTillPrompt(): void {
7170
* @throws NoLoginServerException
7271
* @throws AccessDeniedException
7372
*/
74-
public function read(callable $callback = null): array {
73+
public function read(): array {
7574
if (!$this->isValid()) {
7675
throw new ConnectionException('Connection not valid');
7776
}
78-
$promptLine = $this->readLine(); //first line is prompt
79-
if ($promptLine === false) {
80-
$this->unknownError($promptLine);
81-
}
82-
$this->parser->checkConnectionError($promptLine);
83-
84-
$output = [];
85-
if (!$this->isPrompt($promptLine)) {
86-
$line = $promptLine;
87-
} else {
88-
$line = $this->readLine();
89-
}
90-
if ($line === false) {
91-
$this->unknownError($promptLine);
92-
}
93-
while ($line !== false && !$this->isPrompt($line)) { //next prompt functions as delimiter
94-
if (is_callable($callback)) {
95-
$result = $callback($line);
96-
if ($result === false) { // allow the callback to close the connection for infinite running commands
97-
$this->close(true);
98-
break;
99-
}
100-
} else {
101-
$output[] = $line;
102-
}
103-
$line = $this->readLine();
77+
$output = $this->readTillPrompt();
78+
if ($output === false) {
79+
$this->unknownError(false);
10480
}
81+
$output = explode("\n", $output);
82+
// last line contains the prompt
83+
array_pop($output);
10584
return $output;
10685
}
10786

10887
private function isPrompt(string $line): bool {
109-
return mb_substr($line, 0, self::DELIMITER_LENGTH) === self::DELIMITER;
88+
return substr($line, 0, self::DELIMITER_LENGTH) === self::DELIMITER;
11089
}
11190

11291
/**

src/Wrapped/NotifyHandler.php

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,20 @@ public function getChanges(): array {
6565
*/
6666
public function listen(callable $callback): void {
6767
if ($this->listening) {
68-
$this->connection->read(function (string $line) use ($callback): bool {
68+
while (true) {
69+
$line = $this->connection->readLine();
70+
if ($line === false) {
71+
break;
72+
}
6973
$this->checkForError($line);
7074
$change = $this->parseChangeLine($line);
7175
if ($change) {
7276
$result = $callback($change);
73-
return $result === false ? false : true;
74-
} else {
75-
return true;
77+
if ($result === false) {
78+
break;
79+
}
7680
}
77-
});
81+
};
7882
}
7983
}
8084

src/Wrapped/RawConnection.php

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ public function connect(): void {
7171

7272
setlocale(LC_ALL, Server::LOCALE);
7373
$env = array_merge($this->env, [
74-
'CLI_FORCE_INTERACTIVE' => 'y', // Needed or the prompt isn't displayed!!
74+
'CLI_FORCE_INTERACTIVE' => 'y', // Make sure the prompt is displayed
75+
'CLI_NO_READLINE' => 1, // Not all distros build smbclient with readline, disable it to get consistent behaviour
7576
'LC_ALL' => Server::LOCALE,
7677
'LANG' => Server::LOCALE,
7778
'COLUMNS' => 8192 // prevent smbclient from line-wrapping it's output
@@ -109,13 +110,30 @@ public function write(string $input) {
109110
return $result;
110111
}
111112

113+
/**
114+
* read output till the next prompt
115+
*
116+
* @return string|false
117+
*/
118+
public function readTillPrompt() {
119+
$output = "";
120+
do {
121+
$chunk = $this->readLine('\> ');
122+
if ($chunk === false) {
123+
return false;
124+
}
125+
$output .= $chunk;
126+
} while (strlen($chunk) == 4096 && strpos($chunk, "smb:") === false);
127+
return $output;
128+
}
129+
112130
/**
113131
* read a line of output
114132
*
115133
* @return string|false
116134
*/
117-
public function readLine() {
118-
return stream_get_line($this->getOutputStream(), 4086, "\n");
135+
public function readLine(string $end = "\n") {
136+
return stream_get_line($this->getOutputStream(), 4096, $end);
119137
}
120138

121139
/**

src/Wrapped/Share.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -345,11 +345,17 @@ public function read(string $source) {
345345
// since returned stream is closed by the caller we need to create a new instance
346346
// since we can't re-use the same file descriptor over multiple calls
347347
$connection = $this->getConnection();
348+
stream_set_blocking($connection->getOutputStream(), false);
348349

349350
$connection->write('get ' . $source . ' ' . $this->system->getFD(5));
350351
$connection->write('exit');
351352
$fh = $connection->getFileOutputStream();
352-
stream_context_set_option($fh, 'file', 'connection', $connection);
353+
$fh = CallbackWrapper::wrap($fh, function() use ($connection) {
354+
$connection->write('');
355+
});
356+
if (!is_resource($fh)) {
357+
throw new Exception("Failed to wrap file output");
358+
}
353359
return $fh;
354360
}
355361

@@ -374,7 +380,9 @@ public function write(string $target) {
374380

375381
// use a close callback to ensure the upload is finished before continuing
376382
// this also serves as a way to keep the connection in scope
377-
$stream = CallbackWrapper::wrap($fh, null, null, function () use ($connection) {
383+
$stream = CallbackWrapper::wrap($fh, function() use ($connection) {
384+
$connection->write('');
385+
}, null, function () use ($connection) {
378386
$connection->close(false); // dont terminate, give the upload some time
379387
});
380388
if (is_resource($stream)) {
@@ -446,7 +454,7 @@ public function notify(string $path): INotifyHandler {
446454
* @return string[]
447455
*/
448456
protected function execute(string $command): array {
449-
$this->connect()->write($command . PHP_EOL);
457+
$this->connect()->write($command);
450458
return $this->connect()->read();
451459
}
452460

tests/AbstractShareTest.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
use Icewind\SMB\BasicAuth;
1212
use Icewind\SMB\Exception\AccessDeniedException;
1313
use Icewind\SMB\Exception\AlreadyExistsException;
14+
use Icewind\SMB\Exception\ConnectException;
15+
use Icewind\SMB\Exception\ConnectionException;
16+
use Icewind\SMB\Exception\ConnectionRefusedException;
1417
use Icewind\SMB\Exception\FileInUseException;
1518
use Icewind\SMB\Exception\ForbiddenException;
1619
use Icewind\SMB\Exception\InvalidPathException;
@@ -46,6 +49,8 @@ abstract class AbstractShareTest extends TestCase {
4649
abstract public function getServerClass(): string;
4750

4851
public function setUp(): void {
52+
// ob_end_flush();
53+
// var_dump($this->getName());
4954
$this->config = json_decode(file_get_contents(__DIR__ . '/config.json'));
5055
$options = new Options();
5156
$options->setMinProtocol(IOptions::PROTOCOL_SMB2);
@@ -784,10 +789,10 @@ public function testWrongUserName() {
784789
$share = $server->getShare($this->config->share);
785790
$share->dir("");
786791
$this->fail("Expected exception");
787-
} catch (AccessDeniedException $e) {
788-
$this->assertTrue(true);
789792
} catch (ForbiddenException $e) {
790793
$this->assertTrue(true);
794+
} catch (ConnectException $e) {
795+
$this->assertTrue(true);
791796
}
792797
}
793798
}

tests/config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"host": "localhost",
2+
"host": "skybox.icewind.link",
33
"user": "test",
44
"password": "test",
55
"share": "test",

0 commit comments

Comments
 (0)