Merge pull request #18287 from owncloud/smb-1.0.3

update icewind/smb to 1.0.4
This commit is contained in:
Thomas Müller 2015-08-18 16:35:20 +02:00
commit 99815c17d9
22 changed files with 136 additions and 1017 deletions

View file

@ -1 +1,4 @@
example.php
icewind/smb/tests
icewind/smb/install_libsmbclient.sh
icewind/smb/.travis.yml

View file

@ -6,7 +6,7 @@
"vendor-dir": "."
},
"require": {
"icewind/smb": "1.0.1",
"icewind/smb": "1.0.4",
"icewind/streams": "0.2"
}
}

View file

@ -1,23 +1,23 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "7b46d64e33feb600c5f0ec830b211e6f",
"hash": "5c612406bc1235075305b09a5d6996a9",
"packages": [
{
"name": "icewind/smb",
"version": "v1.0.1",
"version": "v1.0.4",
"source": {
"type": "git",
"url": "https://github.com/icewind1991/SMB.git",
"reference": "8041bc1960bf2da94e60b88b34e5c78300eac476"
"reference": "9277bd20262a01b38a33cc7356e98055f2262d32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/icewind1991/SMB/zipball/8041bc1960bf2da94e60b88b34e5c78300eac476",
"reference": "8041bc1960bf2da94e60b88b34e5c78300eac476",
"url": "https://api.github.com/repos/icewind1991/SMB/zipball/9277bd20262a01b38a33cc7356e98055f2262d32",
"reference": "9277bd20262a01b38a33cc7356e98055f2262d32",
"shasum": ""
},
"require": {
@ -45,7 +45,7 @@
}
],
"description": "php wrapper for smbclient and libsmbclient-php",
"time": "2015-04-20 11:16:24"
"time": "2015-08-17 14:20:38"
},
{
"name": "icewind/streams",

View file

@ -43,17 +43,17 @@
},
{
"name": "icewind/smb",
"version": "v1.0.1",
"version_normalized": "1.0.1.0",
"version": "v1.0.4",
"version_normalized": "1.0.4.0",
"source": {
"type": "git",
"url": "https://github.com/icewind1991/SMB.git",
"reference": "8041bc1960bf2da94e60b88b34e5c78300eac476"
"reference": "9277bd20262a01b38a33cc7356e98055f2262d32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/icewind1991/SMB/zipball/8041bc1960bf2da94e60b88b34e5c78300eac476",
"reference": "8041bc1960bf2da94e60b88b34e5c78300eac476",
"url": "https://api.github.com/repos/icewind1991/SMB/zipball/9277bd20262a01b38a33cc7356e98055f2262d32",
"reference": "9277bd20262a01b38a33cc7356e98055f2262d32",
"shasum": ""
},
"require": {
@ -63,7 +63,7 @@
"require-dev": {
"satooshi/php-coveralls": "dev-master"
},
"time": "2015-04-20 11:16:24",
"time": "2015-08-17 14:20:38",
"type": "library",
"installation-source": "source",
"autoload": {

View file

@ -1,50 +0,0 @@
language: php
php:
- 5.3
- 5.4
- 5.5
env:
global:
- CURRENT_DIR=`pwd`
before_install:
- pass=$(perl -e 'print crypt("test", "password")')
- sudo useradd -m -p $pass test
- sudo apt-get update -qq
- sudo apt-get install samba smbclient libsmbclient-dev libsmbclient
- wget -O /tmp/libsmbclient-php.zip https://github.com/eduardok/libsmbclient-php/archive/master.zip
- unzip /tmp/libsmbclient-php.zip -d /tmp
- cd /tmp/libsmbclient-php-master
- phpize && ./configure && make && sudo make install
- echo 'extension="libsmbclient.so"' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- cd $CURRENT_DIR
- chmod go+w $HOME
- printf "%s\n%s\n" test test|sudo smbpasswd -s test
- sudo mkdir /home/test/test
- sudo chown test /home/test/test
- |
echo "[test]
comment = test
path = /home/test
guest ok = yes
writeable = yes
map archive = yes
map system = yes
map hidden = yes
create mask = 0777
inherit permissions = yes" | sudo tee -a /etc/samba/smb.conf
- sudo service smbd restart
- testparm -s
install:
- composer install --dev --no-interaction
script:
- mkdir -p build/logs
- cd tests
- phpunit --coverage-clover ../build/logs/clover.xml --configuration phpunit.xml
after_script:
- cd $CURRENT_DIR
- php vendor/bin/coveralls -v

View file

@ -0,0 +1,26 @@
<?php
/**
* Copyright (c) 2015 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Licensed under the MIT license:
* http://opensource.org/licenses/MIT
*/
namespace Icewind\SMB;
use Icewind\SMB\Exception\InvalidPathException;
abstract class AbstractShare implements IShare {
private $forbiddenCharacters;
public function __construct() {
$this->forbiddenCharacters = array('?', '<', '>', ':', '*', '|', '"', chr(0), "\n", "\r");
}
protected function verifyPath($path) {
foreach ($this->forbiddenCharacters as $char) {
if (strpos($path, $char) !== false) {
throw new InvalidPathException('Invalid path, "' . $char . '" is not allowed');
}
}
}
}

View file

@ -8,8 +8,10 @@
namespace Icewind\SMB;
use Icewind\SMB\Exception\AuthenticationException;
use Icewind\SMB\Exception\ConnectException;
use Icewind\SMB\Exception\ConnectionException;
use Icewind\SMB\Exception\InvalidHostException;
use Icewind\SMB\Exception\NoLoginServerException;
class Connection extends RawConnection {
const DELIMITER = 'smb:';
@ -26,18 +28,25 @@ class Connection extends RawConnection {
/**
* get all unprocessed output from smbclient until the next prompt
*
* @throws ConnectionException
* @return string
* @throws AuthenticationException
* @throws ConnectException
* @throws ConnectionException
* @throws InvalidHostException
* @throws NoLoginServerException
*/
public function read() {
if (!$this->isValid()) {
throw new ConnectionException();
throw new ConnectionException('Connection not valid');
}
$line = $this->readLine(); //first line is prompt
$this->checkConnectionError($line);
$output = array();
$line = $this->readLine();
if ($line === false) {
throw new ConnectException('Unknown error');
}
$length = mb_strlen(self::DELIMITER);
while (mb_substr($line, 0, $length) !== self::DELIMITER) { //next prompt functions as delimiter
$output[] .= $line;
@ -52,20 +61,24 @@ class Connection extends RawConnection {
* @param $line
* @throws AuthenticationException
* @throws InvalidHostException
* @throws NoLoginServerException
*/
private function checkConnectionError($line) {
$line = rtrim($line, ')');
if (substr($line, -23) === ErrorCodes::LogonFailure) {
throw new AuthenticationException();
throw new AuthenticationException('Invalid login');
}
if (substr($line, -26) === ErrorCodes::BadHostName) {
throw new InvalidHostException();
throw new InvalidHostException('Invalid hostname');
}
if (substr($line, -22) === ErrorCodes::Unsuccessful) {
throw new InvalidHostException();
throw new InvalidHostException('Connection unsuccessful');
}
if (substr($line, -28) === ErrorCodes::ConnectionRefused) {
throw new InvalidHostException();
throw new InvalidHostException('Connection refused');
}
if (substr($line, -26) === ErrorCodes::NoLogonServers) {
throw new NoLoginServerException('No login server');
}
}

View file

@ -15,6 +15,7 @@ class ErrorCodes {
const BadHostName = 'NT_STATUS_BAD_NETWORK_NAME';
const Unsuccessful = 'NT_STATUS_UNSUCCESSFUL';
const ConnectionRefused = 'NT_STATUS_CONNECTION_REFUSED';
const NoLogonServers = 'NT_STATUS_NO_LOGON_SERVERS';
const PathNotFound = 'NT_STATUS_OBJECT_PATH_NOT_FOUND';
const NoSuchFile = 'NT_STATUS_NO_SUCH_FILE';

View file

@ -0,0 +1,10 @@
<?php
/**
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Licensed under the MIT license:
* http://opensource.org/licenses/MIT
*/
namespace Icewind\SMB\Exception;
class InvalidPathException extends InvalidRequestException {}

View file

@ -5,5 +5,6 @@
* http://opensource.org/licenses/MIT
*/
date_default_timezone_set('UTC');
require_once __DIR__.'/../vendor/autoload.php';
namespace Icewind\SMB\Exception;
class NoLoginServerException extends ConnectException {}

View file

@ -24,12 +24,7 @@ class NativeServer extends Server {
}
protected function connect() {
$user = $this->getUser();
$workgroup = null;
if (strpos($user, '/')) {
list($workgroup, $user) = explode($user, '/');
}
$this->state->init($workgroup, $user, $this->getPassword());
$this->state->init($this->getWorkgroup(), $this->getUser(), $this->getPassword());
}
/**

View file

@ -7,7 +7,7 @@
namespace Icewind\SMB;
class NativeShare implements IShare {
class NativeShare extends AbstractShare {
/**
* @var Server $server
*/
@ -28,6 +28,7 @@ class NativeShare implements IShare {
* @param string $name
*/
public function __construct($server, $name) {
parent::__construct();
$this->server = $server;
$this->name = $name;
$this->state = new NativeState();
@ -43,15 +44,7 @@ class NativeShare implements IShare {
return;
}
$user = $this->server->getUser();
if (strpos($user, '/')) {
list($workgroup, $user) = explode('/', $user);
} elseif (strpos($user, '\\')) {
list($workgroup, $user) = explode('\\', $user);
} else {
$workgroup = null;
}
$this->state->init($workgroup, $user, $this->server->getPassword());
$this->state->init($this->server->getWorkgroup(), $this->server->getUser(), $this->server->getPassword());
}
/**
@ -64,6 +57,7 @@ class NativeShare implements IShare {
}
private function buildUrl($path) {
$this->verifyPath($path);
$url = sprintf('smb://%s/%s', $this->server->getHost(), $this->name);
if ($path) {
$path = trim($path, '/');
@ -149,6 +143,7 @@ class NativeShare implements IShare {
* @throws \Icewind\SMB\Exception\InvalidTypeException
*/
public function del($path) {
$this->connect();
return $this->state->unlink($this->buildUrl($path));
}

View file

@ -29,6 +29,11 @@ class Server {
*/
protected $password;
/**
* @var string $workgroup
*/
protected $workgroup;
/**
* Check if the smbclient php extension is available
*
@ -45,10 +50,28 @@ class Server {
*/
public function __construct($host, $user, $password) {
$this->host = $host;
list($workgroup, $user) = $this->splitUser($user);
$this->user = $user;
$this->workgroup = $workgroup;
$this->password = $password;
}
/**
* Split workgroup from username
*
* @param $user
* @return string[] [$workgroup, $user]
*/
public function splitUser($user) {
if (strpos($user, '/')) {
return explode('/', $user, 2);
} elseif (strpos($user, '\\')) {
return explode('\\', $user);
} else {
return array(null, $user);
}
}
/**
* @return string
*/
@ -77,6 +100,13 @@ class Server {
return $this->host;
}
/**
* @return string
*/
public function getWorkgroup() {
return $this->workgroup;
}
/**
* @return \Icewind\SMB\IShare[]
*
@ -84,7 +114,8 @@ class Server {
* @throws \Icewind\SMB\Exception\InvalidHostException
*/
public function listShares() {
$command = Server::CLIENT . ' --authentication-file=/proc/self/fd/3' .
$workgroupArgument = ($this->workgroup) ? ' -W ' . escapeshellarg($this->workgroup) : '';
$command = Server::CLIENT . $workgroupArgument . ' --authentication-file=/proc/self/fd/3' .
' -gL ' . escapeshellarg($this->getHost());
$connection = new RawConnection($command);
$connection->writeAuthentication($this->getUser(), $this->getPassword());

View file

@ -7,17 +7,13 @@
namespace Icewind\SMB;
use Icewind\SMB\Exception\AccessDeniedException;
use Icewind\SMB\Exception\AlreadyExistsException;
use Icewind\SMB\Exception\ConnectionException;
use Icewind\SMB\Exception\Exception;
use Icewind\SMB\Exception\FileInUseException;
use Icewind\SMB\Exception\InvalidTypeException;
use Icewind\SMB\Exception\NotEmptyException;
use Icewind\SMB\Exception\NotFoundException;
use Icewind\Streams\CallbackWrapper;
class Share implements IShare {
class Share extends AbstractShare {
/**
* @var Server $server
*/
@ -43,6 +39,7 @@ class Share implements IShare {
* @param string $name
*/
public function __construct($server, $name) {
parent::__construct();
$this->server = $server;
$this->name = $name;
$this->parser = new Parser(new TimeZoneProvider($this->server->getHost()));
@ -57,10 +54,11 @@ class Share implements IShare {
if ($this->connection and $this->connection->isValid()) {
return;
}
$command = sprintf('%s --authentication-file=/proc/self/fd/3 //%s/%s',
$workgroupArgument = ($this->server->getWorkgroup()) ? ' -W ' . escapeshellarg($this->server->getWorkgroup()) : '';
$command = sprintf('%s %s --authentication-file=/proc/self/fd/3 %s',
Server::CLIENT,
$this->server->getHost(),
$this->name
$workgroupArgument,
escapeshellarg('//' . $this->server->getHost() . '/' . $this->name)
);
$this->connection = new Connection($command);
$this->connection->writeAuthentication($this->server->getUser(), $this->server->getPassword());
@ -256,18 +254,18 @@ class Share implements IShare {
*/
public function read($source) {
$source = $this->escapePath($source);
// close the single quote, open a double quote where we put the single quote...
$source = str_replace('\'', '\'"\'"\'', $source);
// 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
$command = sprintf('%s --authentication-file=/proc/self/fd/3 //%s/%s -c \'get %s /proc/self/fd/5\'',
$workgroupArgument = ($this->server->getWorkgroup()) ? ' -W ' . escapeshellarg($this->server->getWorkgroup()) : '';
$command = sprintf('%s %s --authentication-file=/proc/self/fd/3 %s',
Server::CLIENT,
$this->server->getHost(),
$this->name,
$source
$workgroupArgument,
escapeshellarg('//' . $this->server->getHost() . '/' . $this->name)
);
$connection = new Connection($command);
$connection->writeAuthentication($this->server->getUser(), $this->server->getPassword());
$connection->write('get ' . $source . ' /proc/self/fd/5');
$connection->write('exit');
$fh = $connection->getFileOutputStream();
stream_context_set_option($fh, 'file', 'connection', $connection);
return $fh;
@ -284,23 +282,24 @@ class Share implements IShare {
*/
public function write($target) {
$target = $this->escapePath($target);
// close the single quote, open a double quote where we put the single quote...
$target = str_replace('\'', '\'"\'"\'', $target);
// 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
$command = sprintf('%s --authentication-file=/proc/self/fd/3 //%s/%s -c \'put /proc/self/fd/4 %s\'',
$workgroupArgument = ($this->server->getWorkgroup()) ? ' -W ' . escapeshellarg($this->server->getWorkgroup()) : '';
$command = sprintf('%s %s --authentication-file=/proc/self/fd/3 %s',
Server::CLIENT,
$this->server->getHost(),
$this->name,
$target
$workgroupArgument,
escapeshellarg('//' . $this->server->getHost() . '/' . $this->name)
);
$connection = new RawConnection($command);
$connection = new Connection($command);
$connection->writeAuthentication($this->server->getUser(), $this->server->getPassword());
$fh = $connection->getFileInputStream();
$connection->write('put /proc/self/fd/4 ' . $target);
$connection->write('exit');
// use a close callback to ensure the upload is finished before continuing
// this also serves as a way to keep the connection in scope
return CallbackWrapper::wrap($fh, null, null, function () use ($connection) {
return CallbackWrapper::wrap($fh, null, null, function () use ($connection, $target) {
$connection->close(false); // dont terminate, give the upload some time
});
}
@ -378,6 +377,7 @@ class Share implements IShare {
* @return string
*/
protected function escapePath($path) {
$this->verifyPath($path);
if ($path === '/') {
$path = '';
}

View file

@ -1,539 +0,0 @@
<?php
/**
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Licensed under the MIT license:
* http://opensource.org/licenses/MIT
*/
namespace Icewind\SMB\Test;
use Icewind\SMB\FileInfo;
abstract class AbstractShare extends \PHPUnit_Framework_TestCase {
/**
* @var \Icewind\SMB\Server $server
*/
protected $server;
/**
* @var \Icewind\SMB\IShare $share
*/
protected $share;
/**
* @var string $root
*/
protected $root;
protected $config;
public function tearDown() {
try {
if ($this->share) {
$this->cleanDir($this->root);
}
unset($this->share);
} catch (\Exception $e) {
unset($this->share);
throw $e;
}
}
public function nameProvider() {
// / ? < > \ : * | " are illegal characters in path on windows
return array(
array('simple'),
array('with spaces_and-underscores'),
array("single'quote'"),
array('日本語'),
array('url %2F +encode'),
array('a somewhat longer filename than the other with more charaters as the all the other filenames'),
array('$as#d€££Ö€ßœĚęĘĞĜΣΥΦΩΫ')
);
}
public function fileDataProvider() {
return array(
array('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua'),
array('Mixed language, 日本語 が わからか and Various _/* characters \\|” €')
);
}
public function nameAndDataProvider() {
$names = $this->nameProvider();
$data = $this->fileDataProvider();
$result = array();
foreach ($names as $name) {
foreach ($data as $text) {
$result[] = array($name[0], $text[0]);
}
}
return $result;
}
public function cleanDir($dir) {
$content = $this->share->dir($dir);
foreach ($content as $metadata) {
if ($metadata->isDirectory()) {
$this->cleanDir($metadata->getPath());
} else {
$this->share->del($metadata->getPath());
}
}
$this->share->rmdir($dir);
}
private function getTextFile($text = '') {
if (!$text) {
$text = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua';
}
$file = tempnam('/tmp', 'smb_test_');
file_put_contents($file, $text);
return $file;
}
public function testListShares() {
$shares = $this->server->listShares();
foreach ($shares as $share) {
if ($share->getName() === $this->config->share) {
return;
}
}
$this->fail('Share "' . $this->config->share . '" not found');
}
public function testRootStartsEmpty() {
$this->assertEquals(array(), $this->share->dir($this->root));
}
/**
* @dataProvider nameProvider
*/
public function testMkdir($name) {
$this->share->mkdir($this->root . '/' . $name);
$dirs = $this->share->dir($this->root);
$this->assertCount(1, $dirs);
$this->assertEquals($name, $dirs[0]->getName());
$this->assertTrue($dirs[0]->isDirectory());
}
/**
* @dataProvider nameProvider
*/
public function testRenameDirectory($name) {
$this->share->mkdir($this->root . '/' . $name);
$this->share->rename($this->root . '/' . $name, $this->root . '/' . $name . '_rename');
$dirs = $this->share->dir($this->root);
$this->assertEquals(1, count($dirs));
$this->assertEquals($name . '_rename', $dirs[0]->getName());
}
/**
* @dataProvider nameProvider
*/
public function testRmdir($name) {
$this->share->mkdir($this->root . '/' . $name);
$this->share->rmdir($this->root . '/' . $name);
$this->assertCount(0, $this->share->dir($this->root));
}
/**
* @dataProvider nameAndDataProvider
*/
public function testPut($name, $text) {
$tmpFile = $this->getTextFile($text);
$size = filesize($tmpFile);
$this->share->put($tmpFile, $this->root . '/' . $name);
unlink($tmpFile);
$files = $this->share->dir($this->root);
$this->assertCount(1, $files);
$this->assertEquals($name, $files[0]->getName());
$this->assertEquals($size, $files[0]->getSize());
$this->assertFalse($files[0]->isDirectory());
}
/**
* @dataProvider nameProvider
*/
public function testRenameFile($name) {
$tmpFile = $this->getTextFile();
$this->share->put($tmpFile, $this->root . '/' . $name);
unlink($tmpFile);
$this->share->rename($this->root . '/' . $name, $this->root . '/' . $name . '_renamed');
$files = $this->share->dir($this->root);
$this->assertEquals(1, count($files));
$this->assertEquals($name . '_renamed', $files[0]->getName());
}
/**
* @dataProvider nameAndDataProvider
*/
public function testGet($name, $text) {
$tmpFile = $this->getTextFile($text);
$this->share->put($tmpFile, $this->root . '/' . $name);
unlink($tmpFile);
$targetFile = tempnam('/tmp', 'smb_test_');
$this->share->get($this->root . '/' . $name, $targetFile);
$this->assertEquals($text, file_get_contents($targetFile));
unlink($targetFile);
}
/**
* @dataProvider nameProvider
*/
public function testDel($name) {
$tmpFile = $this->getTextFile();
$this->share->put($tmpFile, $this->root . '/' . $name);
unlink($tmpFile);
$this->share->del($this->root . '/' . $name);
$this->assertCount(0, $this->share->dir($this->root));
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testCreateFolderInNonExistingFolder() {
$this->share->mkdir($this->root . '/foo/bar');
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testRemoveFolderInNonExistingFolder() {
$this->share->rmdir($this->root . '/foo/bar');
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testRemoveNonExistingFolder() {
$this->share->rmdir($this->root . '/foo');
}
/**
* @expectedException \Icewind\SMB\Exception\AlreadyExistsException
*/
public function testCreateExistingFolder() {
$this->share->mkdir($this->root . '/bar');
$this->share->mkdir($this->root . '/bar');
$this->share->rmdir($this->root . '/bar');
}
/**
* @expectedException \Icewind\SMB\Exception\InvalidTypeException
*/
public function testCreateFileExistingFolder() {
$this->share->mkdir($this->root . '/bar');
$this->share->put($this->getTextFile(), $this->root . '/bar');
$this->share->rmdir($this->root . '/bar');
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testCreateFileInNonExistingFolder() {
$this->share->put($this->getTextFile(), $this->root . '/foo/bar');
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testTestRemoveNonExistingFile() {
$this->share->del($this->root . '/foo');
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testDownloadNonExistingFile() {
$this->share->get($this->root . '/foo', '/dev/null');
}
/**
* @expectedException \Icewind\SMB\Exception\InvalidTypeException
*/
public function testDownloadFolder() {
$this->share->mkdir($this->root . '/foobar');
$this->share->get($this->root . '/foobar', '/dev/null');
$this->share->rmdir($this->root . '/foobar');
}
/**
* @expectedException \Icewind\SMB\Exception\InvalidTypeException
*/
public function testDelFolder() {
$this->share->mkdir($this->root . '/foobar');
$this->share->del($this->root . '/foobar');
$this->share->rmdir($this->root . '/foobar');
}
/**
* @expectedException \Icewind\SMB\Exception\InvalidTypeException
*/
public function testRmdirFile() {
$this->share->put($this->getTextFile(), $this->root . '/foobar');
$this->share->rmdir($this->root . '/foobar');
$this->share->del($this->root . '/foobar');
}
/**
* @expectedException \Icewind\SMB\Exception\NotEmptyException
*/
public function testRmdirNotEmpty() {
$this->share->mkdir($this->root . '/foobar');
$this->share->put($this->getTextFile(), $this->root . '/foobar/asd');
$this->share->rmdir($this->root . '/foobar');
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testDirNonExisting() {
$this->share->dir('/foobar/asd');
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testRmDirNonExisting() {
$this->share->rmdir('/foobar/asd');
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testRenameNonExisting() {
$this->share->rename('/foobar/asd', '/foobar/bar');
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testRenameTargetNonExisting() {
$txt = $this->getTextFile();
$this->share->put($txt, $this->root . '/foo.txt');
unlink($txt);
$this->share->rename($this->root . '/foo.txt', $this->root . '/bar/foo.txt');
}
public function testModifiedDate() {
$now = time();
$this->share->put($this->getTextFile(), $this->root . '/foo.txt');
$dir = $this->share->dir($this->root);
$mtime = $dir[0]->getMTime();
$this->assertTrue(abs($now - $mtime) <= 2, 'Modified time differs by ' . abs($now - $mtime) . ' seconds');
$this->share->del($this->root . '/foo.txt');
}
/**
* @dataProvider nameAndDataProvider
*/
public function testReadStream($name, $text) {
$sourceFile = $this->getTextFile($text);
$this->share->put($sourceFile, $this->root . '/' . $name);
$fh = $this->share->read($this->root . '/' . $name);
$content = stream_get_contents($fh);
fclose($fh);
$this->share->del($this->root . '/' . $name);
$this->assertEquals(file_get_contents($sourceFile), $content);
}
/**
* @dataProvider nameAndDataProvider
*/
public function testWriteStream($name, $text) {
$fh = $this->share->write($this->root . '/' . $name);
fwrite($fh, $text);
fclose($fh);
$tmpFile1 = tempnam('/tmp', 'smb_test_');
$this->share->get($this->root . '/' . $name, $tmpFile1);
$this->assertEquals($text, file_get_contents($tmpFile1));
$this->share->del($this->root . '/' . $name);
unlink($tmpFile1);
}
public function testDir() {
$txtFile = $this->getTextFile();
$this->share->mkdir($this->root . '/dir');
$this->share->put($txtFile, $this->root . '/file.txt');
unlink($txtFile);
$dir = $this->share->dir($this->root);
if ($dir[0]->getName() === 'dir') {
$dirEntry = $dir[0];
} else {
$dirEntry = $dir[1];
}
$this->assertTrue($dirEntry->isDirectory());
$this->assertFalse($dirEntry->isReadOnly());
$this->assertFalse($dirEntry->isReadOnly());
if ($dir[0]->getName() === 'file.txt') {
$fileEntry = $dir[0];
} else {
$fileEntry = $dir[1];
}
$this->assertFalse($fileEntry->isDirectory());
$this->assertFalse($fileEntry->isReadOnly());
$this->assertFalse($fileEntry->isReadOnly());
}
/**
* @dataProvider nameProvider
*/
public function testStat($name) {
$txtFile = $this->getTextFile();
$size = filesize($txtFile);
$this->share->put($txtFile, $this->root . '/' . $name);
unlink($txtFile);
$info = $this->share->stat($this->root . '/' . $name);
$this->assertEquals($size, $info->getSize());
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testStatNonExisting() {
$this->share->stat($this->root . '/fo.txt');
}
/**
* note setting archive and system bit is not supported
*
* @dataProvider nameProvider
*/
public function testSetMode($name) {
$txtFile = $this->getTextFile();
$this->share->put($txtFile, $this->root . '/' . $name);
$this->share->setMode($this->root . '/' . $name, FileInfo::MODE_NORMAL);
$info = $this->share->stat($this->root . '/' . $name);
$this->assertFalse($info->isReadOnly());
$this->assertFalse($info->isArchived());
$this->assertFalse($info->isSystem());
$this->assertFalse($info->isHidden());
$this->share->setMode($this->root . '/' . $name, FileInfo::MODE_READONLY);
$info = $this->share->stat($this->root . '/' . $name);
$this->assertTrue($info->isReadOnly());
$this->assertFalse($info->isArchived());
$this->assertFalse($info->isSystem());
$this->assertFalse($info->isHidden());
$this->share->setMode($this->root . '/' . $name, FileInfo::MODE_ARCHIVE);
$info = $this->share->stat($this->root . '/' . $name);
$this->assertFalse($info->isReadOnly());
$this->assertTrue($info->isArchived());
$this->assertFalse($info->isSystem());
$this->assertFalse($info->isHidden());
$this->share->setMode($this->root . '/' . $name, FileInfo::MODE_READONLY | FileInfo::MODE_ARCHIVE);
$info = $this->share->stat($this->root . '/' . $name);
$this->assertTrue($info->isReadOnly());
$this->assertTrue($info->isArchived());
$this->assertFalse($info->isSystem());
$this->assertFalse($info->isHidden());
$this->share->setMode($this->root . '/' . $name, FileInfo::MODE_HIDDEN);
$info = $this->share->stat($this->root . '/' . $name);
$this->assertFalse($info->isReadOnly());
$this->assertFalse($info->isArchived());
$this->assertFalse($info->isSystem());
$this->assertTrue($info->isHidden());
$this->share->setMode($this->root . '/' . $name, FileInfo::MODE_SYSTEM);
$info = $this->share->stat($this->root . '/' . $name);
$this->assertFalse($info->isReadOnly());
$this->assertFalse($info->isArchived());
$this->assertTrue($info->isSystem());
$this->assertFalse($info->isHidden());
$this->share->setMode($this->root . '/' . $name, FileInfo::MODE_NORMAL);
$info = $this->share->stat($this->root . '/' . $name);
$this->assertFalse($info->isReadOnly());
$this->assertFalse($info->isArchived());
$this->assertFalse($info->isSystem());
$this->assertFalse($info->isHidden());
}
public function pathProvider() {
// / ? < > \ : * | " are illegal characters in path on windows
return array(
array('dir/sub/foo.txt'),
array('bar.txt'),
array("single'quote'/sub/foo.txt"),
array('日本語/url %2F +encode/asd.txt'),
array(
'a somewhat longer folder than the other with more charaters as the all the other filenames/' .
'followed by a somewhat long file name after that.txt'
)
);
}
/**
* @dataProvider pathProvider
*/
public function testSubDirs($path) {
$dirs = explode('/', $path);
$name = array_pop($dirs);
$fullPath = '';
foreach ($dirs as $dir) {
$fullPath .= '/' . $dir;
$this->share->mkdir($this->root . $fullPath);
}
$txtFile = $this->getTextFile();
$size = filesize($txtFile);
$this->share->put($txtFile, $this->root . $fullPath . '/' . $name);
unlink($txtFile);
$info = $this->share->stat($this->root . $fullPath . '/' . $name);
$this->assertEquals($size, $info->getSize());
$this->assertFalse($info->isHidden());
}
public function testDelAfterStat() {
$name = 'foo.txt';
$txtFile = $this->getTextFile();
$this->share->put($txtFile, $this->root . '/' . $name);
unlink($txtFile);
$this->share->stat($this->root . '/' . $name);
$this->share->del($this->root . '/foo.txt');
}
/**
* @param $name
* @dataProvider nameProvider
*/
public function testDirPaths($name) {
$txtFile = $this->getTextFile();
$this->share->mkdir($this->root . '/' . $name);
$this->share->put($txtFile, $this->root . '/' . $name . '/' . $name);
unlink($txtFile);
$content = $this->share->dir($this->root . '/' . $name);
$this->assertCount(1, $content);
$this->assertEquals($name, $content[0]->getName());
}
public function testStatRoot() {
$info = $this->share->stat('/');
$this->assertInstanceOf('\Icewind\SMB\IFileInfo', $info);
}
}

View file

@ -1,27 +0,0 @@
<?php
/**
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Licensed under the MIT license:
* http://opensource.org/licenses/MIT
*/
namespace Icewind\SMB\Test;
use Icewind\SMB\NativeServer;
class NativeShare extends AbstractShare {
public function setUp() {
if (!function_exists('smbclient_state_new')) {
$this->markTestSkipped('libsmbclient php extension not installed');
}
$this->config = json_decode(file_get_contents(__DIR__ . '/config.json'));
$this->server = new NativeServer($this->config->host, $this->config->user, $this->config->password);
$this->share = $this->server->getShare($this->config->share);
if ($this->config->root) {
$this->root = '/' . $this->config->root . '/' . uniqid();
} else {
$this->root = '/' . uniqid();
}
$this->share->mkdir($this->root);
}
}

View file

@ -1,143 +0,0 @@
<?php
/**
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Licensed under the MIT license:
* http://opensource.org/licenses/MIT
*/
namespace Icewind\SMB\Test;
use Icewind\SMB\NativeServer;
class NativeStream extends \PHPUnit_Framework_TestCase {
/**
* @var \Icewind\SMB\Server $server
*/
protected $server;
/**
* @var \Icewind\SMB\NativeShare $share
*/
protected $share;
/**
* @var string $root
*/
protected $root;
protected $config;
public function setUp() {
if (!function_exists('smbclient_state_new')) {
$this->markTestSkipped('libsmbclient php extension not installed');
}
$this->config = json_decode(file_get_contents(__DIR__ . '/config.json'));
$this->server = new NativeServer($this->config->host, $this->config->user, $this->config->password);
$this->share = $this->server->getShare($this->config->share);
if ($this->config->root) {
$this->root = '/' . $this->config->root . '/' . uniqid();
} else {
$this->root = '/' . uniqid();
}
$this->share->mkdir($this->root);
}
private function getTextFile() {
$text = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua';
$file = tempnam('/tmp', 'smb_test_');
file_put_contents($file, $text);
return $file;
}
public function testSeekTell() {
$sourceFile = $this->getTextFile();
$this->share->put($sourceFile, $this->root . '/foobar');
$fh = $this->share->read($this->root . '/foobar');
$content = fread($fh, 3);
$this->assertEquals('Lor', $content);
fseek($fh, -2, SEEK_CUR);
$content = fread($fh, 3);
$this->assertEquals('ore', $content);
fseek($fh, 3, SEEK_SET);
$content = fread($fh, 3);
$this->assertEquals('em ', $content);
fseek($fh, -3, SEEK_END);
$content = fread($fh, 3);
$this->assertEquals('qua', $content);
fseek($fh, -3, SEEK_END);
$this->assertEquals(120, ftell($fh));
}
public function testStat() {
$sourceFile = $this->getTextFile();
$this->share->put($sourceFile, $this->root . '/foobar');
$fh = $this->share->read($this->root . '/foobar');
$stat = fstat($fh);
$this->assertEquals(filesize($sourceFile), $stat['size']);
unlink($sourceFile);
}
public function testTruncate() {
if (version_compare(phpversion(), '5.4.0', '<')) {
$this->markTestSkipped('php <5.4 doesn\'t support truncate for stream wrappers');
}
$fh = $this->share->write($this->root . '/foobar');
fwrite($fh, 'foobar');
ftruncate($fh, 3);
fclose($fh);
$fh = $this->share->read($this->root . '/foobar');
$this->assertEquals('foo', stream_get_contents($fh));
}
public function testEOF() {
if (version_compare(phpversion(), '5.4.0', '<')) {
$this->markTestSkipped('php <5.4 doesn\'t support truncate for stream wrappers');
}
$fh = $this->share->write($this->root . '/foobar');
fwrite($fh, 'foobar');
fclose($fh);
$fh = $this->share->read($this->root . '/foobar');
fread($fh, 3);
$this->assertFalse(feof($fh));
fread($fh, 5);
$this->assertTrue(feof($fh));
}
public function testLockUnsupported() {
$fh = $this->share->write($this->root . '/foobar');
$this->assertFalse(flock($fh, LOCK_SH));
}
public function testSetOptionUnsupported() {
$fh = $this->share->write($this->root . '/foobar');
$this->assertFalse(stream_set_blocking($fh, false));
}
public function tearDown() {
if ($this->share) {
$this->cleanDir($this->root);
}
unset($this->share);
}
public function cleanDir($dir) {
$content = $this->share->dir($dir);
foreach ($content as $metadata) {
if ($metadata->isDirectory()) {
$this->cleanDir($metadata->getPath());
} else {
$this->share->del($metadata->getPath());
}
}
$this->share->rmdir($dir);
}
}

View file

@ -1,103 +0,0 @@
<?php
/**
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Licensed under the MIT license:
* http://opensource.org/licenses/MIT
*/
namespace Icewind\SMB\Test;
use Icewind\SMB\FileInfo;
class Parser extends \PHPUnit_Framework_TestCase {
public function modeProvider() {
return array(
array('D', FileInfo::MODE_DIRECTORY),
array('A', FileInfo::MODE_ARCHIVE),
array('S', FileInfo::MODE_SYSTEM),
array('H', FileInfo::MODE_HIDDEN),
array('R', FileInfo::MODE_READONLY),
array('N', FileInfo::MODE_NORMAL),
array('RA', FileInfo::MODE_READONLY | FileInfo::MODE_ARCHIVE),
array('RAH', FileInfo::MODE_READONLY | FileInfo::MODE_ARCHIVE | FileInfo::MODE_HIDDEN)
);
}
/**
* @param string $timeZone
* @return \Icewind\SMB\TimeZoneProvider
*/
private function getTimeZoneProvider($timeZone) {
$mock = $this->getMockBuilder('\Icewind\SMB\TimeZoneProvider')
->disableOriginalConstructor()
->getMock();
$mock->expects($this->any())
->method('get')
->will($this->returnValue($timeZone));
return $mock;
}
/**
* @dataProvider modeProvider
*/
public function testParseMode($string, $mode) {
$parser = new \Icewind\SMB\Parser($this->getTimeZoneProvider('UTC'));
$this->assertEquals($mode, $parser->parseMode($string), 'Failed parsing ' . $string);
}
public function statProvider() {
return array(
array(
array(
'altname: test.txt',
'create_time: Sat Oct 12 07:05:58 PM 2013 CEST',
'access_time: Tue Oct 15 02:58:48 PM 2013 CEST',
'write_time: Sat Oct 12 07:05:58 PM 2013 CEST',
'change_time: Sat Oct 12 07:05:58 PM 2013 CEST',
'attributes: (80)',
'stream: [::$DATA], 29634 bytes'
),
array(
'mtime' => strtotime('12 Oct 2013 19:05:58 CEST'),
'mode' => FileInfo::MODE_NORMAL,
'size' => 29634
)
)
);
}
/**
* @dataProvider statProvider
*/
public function testStat($output, $stat) {
$parser = new \Icewind\SMB\Parser($this->getTimeZoneProvider('UTC'));
$this->assertEquals($stat, $parser->parseStat($output));
}
public function dirProvider() {
return array(
array(
array(
' . D 0 Tue Aug 26 19:11:56 2014',
' .. DR 0 Sun Oct 28 15:24:02 2012',
' c.pdf N 29634 Sat Oct 12 19:05:58 2013',
'',
' 62536 blocks of size 8388608. 57113 blocks available'
),
array(
new FileInfo('/c.pdf', 'c.pdf', 29634, strtotime('12 Oct 2013 19:05:58 CEST'),
FileInfo::MODE_NORMAL)
)
)
);
}
/**
* @dataProvider dirProvider
*/
public function testDir($output, $dir) {
$parser = new \Icewind\SMB\Parser($this->getTimeZoneProvider('CEST'));
$this->assertEquals($dir, $parser->parseDir($output, ''));
}
}

View file

@ -1,57 +0,0 @@
<?php
/**
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Licensed under the MIT license:
* http://opensource.org/licenses/MIT
*/
namespace Icewind\SMB\Test;
class Server extends \PHPUnit_Framework_TestCase {
/**
* @var \Icewind\SMB\Server $server
*/
private $server;
private $config;
public function setUp() {
$this->config = json_decode(file_get_contents(__DIR__ . '/config.json'));
$this->server = new \Icewind\SMB\Server($this->config->host, $this->config->user, $this->config->password);
}
public function testListShares() {
$shares = $this->server->listShares();
foreach ($shares as $share) {
if ($share->getName() === $this->config->share) {
return;
}
}
$this->fail('Share "' . $this->config->share . '" not found');
}
/**
* @expectedException \Icewind\SMB\Exception\AuthenticationException
*/
public function testWrongUserName() {
$this->markTestSkipped('This fails for no reason on travis');
$server = new \Icewind\SMB\Server($this->config->host, uniqid(), uniqid());
$server->listShares();
}
/**
* @expectedException \Icewind\SMB\Exception\AuthenticationException
*/
public function testWrongPassword() {
$server = new \Icewind\SMB\Server($this->config->host, $this->config->user, uniqid());
$server->listShares();
}
/**
* @expectedException \Icewind\SMB\Exception\InvalidHostException
*/
public function testWrongHost() {
$server = new \Icewind\SMB\Server(uniqid(), $this->config->user, $this->config->password);
$server->listShares();
}
}

View file

@ -1,24 +0,0 @@
<?php
/**
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Licensed under the MIT license:
* http://opensource.org/licenses/MIT
*/
namespace Icewind\SMB\Test;
use Icewind\SMB\Server as NormalServer;
class Share extends AbstractShare {
public function setUp() {
$this->config = json_decode(file_get_contents(__DIR__ . '/config.json'));
$this->server = new NormalServer($this->config->host, $this->config->user, $this->config->password);
$this->share = $this->server->getShare($this->config->share);
if ($this->config->root) {
$this->root = '/' . $this->config->root . '/' . uniqid();
} else {
$this->root = '/' . uniqid();
}
$this->share->mkdir($this->root);
}
}

View file

@ -1,7 +0,0 @@
{
"host": "localhost",
"user": "test",
"password": "test",
"share": "test",
"root": "test"
}

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<phpunit bootstrap="bootstrap.php">
<testsuite name='SMB'>
<directory suffix='.php'>./</directory>
</testsuite>
</phpunit>