@@ -8,7 +8,7 @@ | |||
"classmap-authoritative": true | |||
}, | |||
"require": { | |||
"icewind/smb": "1.0.8", | |||
"icewind/smb": "1.1.0", | |||
"icewind/streams": "0.4" | |||
} | |||
} |
@@ -4,21 +4,21 @@ | |||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", | |||
"This file is @generated automatically" | |||
], | |||
"hash": "1671a5ec7bef407432d42775f898dc34", | |||
"content-hash": "9d995f0d55bee8a3b344a3c685e7b4a4", | |||
"hash": "8de0823d3d0a167ee24450a111cb67b9", | |||
"content-hash": "6733058865c1765823b31cfbb24552e1", | |||
"packages": [ | |||
{ | |||
"name": "icewind/smb", | |||
"version": "v1.0.8", | |||
"version": "v1.1.0", | |||
"source": { | |||
"type": "git", | |||
"url": "https://github.com/icewind1991/SMB.git", | |||
"reference": "764f3fc793a904eb937d619ad097fb076ff199cd" | |||
"reference": "822f924967c68228555cea84ea44765f8e85c601" | |||
}, | |||
"dist": { | |||
"type": "zip", | |||
"url": "https://api.github.com/repos/icewind1991/SMB/zipball/764f3fc793a904eb937d619ad097fb076ff199cd", | |||
"reference": "764f3fc793a904eb937d619ad097fb076ff199cd", | |||
"url": "https://api.github.com/repos/icewind1991/SMB/zipball/822f924967c68228555cea84ea44765f8e85c601", | |||
"reference": "822f924967c68228555cea84ea44765f8e85c601", | |||
"shasum": "" | |||
}, | |||
"require": { | |||
@@ -47,7 +47,7 @@ | |||
} | |||
], | |||
"description": "php wrapper for smbclient and libsmbclient-php", | |||
"time": "2016-03-17 13:29:58" | |||
"time": "2016-04-26 13:26:39" | |||
}, | |||
{ | |||
"name": "icewind/streams", |
@@ -1,5 +1,5 @@ | |||
Copyright (c) 2015 Nils Adermann, Jordi Boggiano | |||
Copyright (c) 2016 Nils Adermann, Jordi Boggiano | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal |
@@ -44,17 +44,17 @@ | |||
}, | |||
{ | |||
"name": "icewind/smb", | |||
"version": "v1.0.8", | |||
"version_normalized": "1.0.8.0", | |||
"version": "v1.1.0", | |||
"version_normalized": "1.1.0.0", | |||
"source": { | |||
"type": "git", | |||
"url": "https://github.com/icewind1991/SMB.git", | |||
"reference": "764f3fc793a904eb937d619ad097fb076ff199cd" | |||
"reference": "822f924967c68228555cea84ea44765f8e85c601" | |||
}, | |||
"dist": { | |||
"type": "zip", | |||
"url": "https://api.github.com/repos/icewind1991/SMB/zipball/764f3fc793a904eb937d619ad097fb076ff199cd", | |||
"reference": "764f3fc793a904eb937d619ad097fb076ff199cd", | |||
"url": "https://api.github.com/repos/icewind1991/SMB/zipball/822f924967c68228555cea84ea44765f8e85c601", | |||
"reference": "822f924967c68228555cea84ea44765f8e85c601", | |||
"shasum": "" | |||
}, | |||
"require": { | |||
@@ -65,7 +65,7 @@ | |||
"phpunit/phpunit": "^4.8", | |||
"satooshi/php-coveralls": "v1.0.0" | |||
}, | |||
"time": "2016-03-17 13:29:58", | |||
"time": "2016-04-26 13:26:39", | |||
"type": "library", | |||
"installation-source": "source", | |||
"autoload": { |
@@ -15,6 +15,7 @@ use Icewind\SMB\Exception\NoLoginServerException; | |||
class Connection extends RawConnection { | |||
const DELIMITER = 'smb:'; | |||
const DELIMITER_LENGTH = 4; | |||
/** | |||
* send input to smbclient | |||
@@ -28,6 +29,7 @@ class Connection extends RawConnection { | |||
/** | |||
* get all unprocessed output from smbclient until the next prompt | |||
* | |||
* @param callable $callback (optional) callback to call for every line read | |||
* @return string | |||
* @throws AuthenticationException | |||
* @throws ConnectException | |||
@@ -35,7 +37,7 @@ class Connection extends RawConnection { | |||
* @throws InvalidHostException | |||
* @throws NoLoginServerException | |||
*/ | |||
public function read() { | |||
public function read(callable $callback = null) { | |||
if (!$this->isValid()) { | |||
throw new ConnectionException('Connection not valid'); | |||
} | |||
@@ -45,25 +47,49 @@ class Connection extends RawConnection { | |||
$output = array(); | |||
$line = $this->readLine(); | |||
if ($line === false) { | |||
if ($promptLine) { //maybe we have some error we missed on the previous line | |||
throw new ConnectException('Unknown error (' . $promptLine . ')'); | |||
} else { | |||
$error = $this->readError(); // maybe something on stderr | |||
if ($error) { | |||
throw new ConnectException('Unknown error (' . $error . ')'); | |||
} else { | |||
throw new ConnectException('Unknown error'); | |||
$this->unknownError($promptLine); | |||
} | |||
while (!$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); | |||
} | |||
} else { | |||
$output[] .= $line; | |||
} | |||
} | |||
$length = mb_strlen(self::DELIMITER); | |||
while (mb_substr($line, 0, $length) !== self::DELIMITER) { //next prompt functions as delimiter | |||
$output[] .= $line; | |||
$line = $this->readLine(); | |||
} | |||
return $output; | |||
} | |||
/** | |||
* Check | |||
* | |||
* @param $line | |||
* @return bool | |||
*/ | |||
private function isPrompt($line) { | |||
return mb_substr($line, 0, self::DELIMITER_LENGTH) === self::DELIMITER || $line === false; | |||
} | |||
/** | |||
* @param string $promptLine (optional) prompt line that might contain some info about the error | |||
* @throws ConnectException | |||
*/ | |||
private function unknownError($promptLine = '') { | |||
if ($promptLine) { //maybe we have some error we missed on the previous line | |||
throw new ConnectException('Unknown error (' . $promptLine . ')'); | |||
} else { | |||
$error = $this->readError(); // maybe something on stderr | |||
if ($error) { | |||
throw new ConnectException('Unknown error (' . $error . ')'); | |||
} else { | |||
throw new ConnectException('Unknown error'); | |||
} | |||
} | |||
} | |||
/** | |||
* check if the first line holds a connection failure | |||
* |
@@ -8,6 +8,17 @@ | |||
namespace Icewind\SMB; | |||
interface IShare { | |||
// https://msdn.microsoft.com/en-us/library/dn392331.aspx | |||
const NOTIFY_ADDED = 1; | |||
const NOTIFY_REMOVED = 2; | |||
const NOTIFY_MODIFIED = 3; | |||
const NOTIFY_RENAMED_OLD = 4; | |||
const NOTIFY_RENAMED_NEW = 5; | |||
const NOTIFY_ADDED_STREAM = 6; | |||
const NOTIFY_REMOVED_STREAM = 7; | |||
const NOTIFY_MODIFIED_STREAM = 8; | |||
const NOTIFY_REMOVED_BY_DELETE = 9; | |||
/** | |||
* Get the name of the share | |||
* | |||
@@ -131,4 +142,11 @@ interface IShare { | |||
* @return mixed | |||
*/ | |||
public function setMode($path, $mode); | |||
/** | |||
* @param string $path | |||
* @param callable $callback callable which will be called for each received change | |||
* @return mixed | |||
*/ | |||
public function notify($path, callable $callback); | |||
} |
@@ -301,6 +301,18 @@ class NativeShare extends AbstractShare { | |||
return $this->setAttribute($path, 'system.dos_attr.mode', $mode); | |||
} | |||
/** | |||
* @param string $path | |||
* @param callable $callback callable which will be called for each received change | |||
* @return mixed | |||
*/ | |||
public function notify($path, callable $callback) { | |||
// php-smbclient does support notify (https://github.com/eduardok/libsmbclient-php/issues/29) | |||
// so we use the smbclient based backend for this | |||
$share = new Share($this->server, $this->getName()); | |||
$share->notify($path, $callback); | |||
} | |||
public function __destruct() { | |||
unset($this->state); | |||
} |
@@ -51,6 +51,22 @@ class Share extends AbstractShare { | |||
$this->parser = new Parser(new TimeZoneProvider($this->server->getHost(), $this->system)); | |||
} | |||
protected function getConnection() { | |||
$workgroupArgument = ($this->server->getWorkgroup()) ? ' -W ' . escapeshellarg($this->server->getWorkgroup()) : ''; | |||
$command = sprintf('stdbuf -o0 %s %s --authentication-file=%s %s', | |||
$this->system->getSmbclientPath(), | |||
$workgroupArgument, | |||
System::getFD(3), | |||
escapeshellarg('//' . $this->server->getHost() . '/' . $this->name) | |||
); | |||
$connection = new Connection($command); | |||
$connection->writeAuthentication($this->server->getUser(), $this->server->getPassword()); | |||
if (!$connection->isValid()) { | |||
throw new ConnectionException(); | |||
} | |||
return $connection; | |||
} | |||
/** | |||
* @throws \Icewind\SMB\Exception\ConnectionException | |||
* @throws \Icewind\SMB\Exception\AuthenticationException | |||
@@ -60,18 +76,7 @@ class Share extends AbstractShare { | |||
if ($this->connection and $this->connection->isValid()) { | |||
return; | |||
} | |||
$workgroupArgument = ($this->server->getWorkgroup()) ? ' -W ' . escapeshellarg($this->server->getWorkgroup()) : ''; | |||
$command = sprintf('%s %s --authentication-file=%s %s', | |||
$this->system->getSmbclientPath(), | |||
$workgroupArgument, | |||
System::getFD(3), | |||
escapeshellarg('//' . $this->server->getHost() . '/' . $this->name) | |||
); | |||
$this->connection = new Connection($command); | |||
$this->connection->writeAuthentication($this->server->getUser(), $this->server->getPassword()); | |||
if (!$this->connection->isValid()) { | |||
throw new ConnectionException(); | |||
} | |||
$this->connection = $this->getConnection(); | |||
} | |||
protected function reconnect() { | |||
@@ -344,6 +349,26 @@ class Share extends AbstractShare { | |||
return $this->parseOutput($output, $path); | |||
} | |||
/** | |||
* @param string $path | |||
* @param callable $callback callable which will be called for each received change | |||
* @return mixed | |||
*/ | |||
public function notify($path, callable $callback) { | |||
$connection = $this->getConnection(); // use a fresh connection since the notify command blocks the process | |||
$command = 'notify ' . $this->escapePath($path); | |||
$connection->write($command . PHP_EOL); | |||
$connection->read(function ($line) use ($callback, $path) { | |||
$code = (int)substr($line, 0, 4); | |||
$subPath = substr($line, 5); | |||
if ($path === '') { | |||
return $callback($code, $subPath); | |||
} else { | |||
return $callback($code, $path . '/' . $subPath); | |||
} | |||
}); | |||
} | |||
/** | |||
* @param string $command | |||
* @return array | |||
@@ -370,7 +395,7 @@ class Share extends AbstractShare { | |||
* @return bool | |||
*/ | |||
protected function parseOutput($lines, $path = '') { | |||
$this->parser->checkForError($lines, $path); | |||
return $this->parser->checkForError($lines, $path); | |||
} | |||
/** |