pax_global_header00006660000000000000000000000064126477037040014524gustar00rootroot0000000000000052 comment=acb94a0a85290d653cd64c883175b855ada5022f SMB-1.0.5/000077500000000000000000000000001264770370400121505ustar00rootroot00000000000000SMB-1.0.5/.gitignore000066400000000000000000000000331264770370400141340ustar00rootroot00000000000000.idea vendor composer.lock SMB-1.0.5/.travis.yml000066400000000000000000000022231264770370400142600ustar00rootroot00000000000000language: php php: - 5.3 - 5.4 - 5.5 - 5.6 - 7.0 env: matrix: allow_failures: - php: 7.0 env: global: - CURRENT_DIR=`pwd` matrix: - BACKEND=smbclient - BACKEND=libsmbclient 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 - if [ "$BACKEND" == 'libsmbclient' ]; then ./install_libsmbclient.sh; fi - 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 SMB-1.0.5/LICENSE.txt000066400000000000000000000020711264770370400137730ustar00rootroot00000000000000Copyright (c) 2014 Robin Appelman Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SMB-1.0.5/README.md000066400000000000000000000057611264770370400134400ustar00rootroot00000000000000SMB === [![Coverage Status](https://img.shields.io/coveralls/icewind1991/SMB.svg)](https://coveralls.io/r/icewind1991/SMB?branch=master) [![Build Status](https://travis-ci.org/icewind1991/SMB.svg?branch=master)](https://travis-ci.org/icewind1991/SMB) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/icewind1991/SMB/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/icewind1991/SMB/?branch=master) PHP wrapper for `smbclient` and [`libsmbclient-php`](https://github.com/eduardok/libsmbclient-php) - Reuses a single `smbclient` instance for multiple requests - Doesn't leak the password to the process list - Simple 1-on-1 mapping of SMB commands - A stream-based api to remove the need for temporary files - Support for using libsmbclient directly trough [`libsmbclient-php`](https://github.com/eduardok/libsmbclient-php) Examples ---- ### Upload a file ### ```php getShare('test'); $share->put($fileToUpload, 'example.txt'); ``` ### Download a file ### ```php getShare('test'); $share->get('example.txt', $target); ``` ### List shares on the remote server ### ```php listShares(); foreach ($shares as $share) { echo $share->getName() . "\n"; } ``` ### List the content of a folder ### ```php getShare('test'); $content = $share->dir('test'); foreach ($content as $info) { echo $name->getName() . "\n"; echo "\tsize :" . $info->getSize() . "\n"; } ``` ### Using read streams ```php getShare('test'); $fh = $share->read('test.txt'); echo fread($fh, 4086); fclose($fh); ``` ### Using write streams ```php getShare('test'); $fh = $share->write('test.txt'); fwrite($fh, 'bar'); fclose($fh); ``` ### Using libsmbclient-php ### Install [libsmbclient-php](https://github.com/eduardok/libsmbclient-php) ```php getShare('test'); $share->put($fileToUpload, 'example.txt'); ``` SMB-1.0.5/composer.json000066400000000000000000000010051264770370400146660ustar00rootroot00000000000000{ "name" : "icewind/smb", "description" : "php wrapper for smbclient and libsmbclient-php", "license" : "MIT", "authors" : [ { "name" : "Robin Appelman", "email": "icewind@owncloud.com" } ], "require" : { "php": ">=5.3", "icewind/streams": "0.2.*" }, "require-dev": { "satooshi/php-coveralls" : "v1.0.0", "phpunit/phpunit": "^4.8" }, "autoload" : { "psr-4": { "Icewind\\SMB\\": "src/", "Icewind\\SMB\\Test\\": "tests/" } } } SMB-1.0.5/example.php000066400000000000000000000006351264770370400143200ustar00rootroot00000000000000getShare('test'); $share->put(__FILE__, 'example.php'); $files = $share->dir('/'); foreach ($files as $file) { echo $file->getName() . "\n"; } SMB-1.0.5/install_libsmbclient.sh000077500000000000000000000005761264770370400167140ustar00rootroot00000000000000#!/usr/bin/env bash sudo apt-get install 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="smbclient.so"' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini SMB-1.0.5/src/000077500000000000000000000000001264770370400127375ustar00rootroot00000000000000SMB-1.0.5/src/AbstractShare.php000066400000000000000000000012651264770370400162020ustar00rootroot00000000000000 * 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'); } } } } SMB-1.0.5/src/Connection.php000066400000000000000000000054401264770370400155520ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ 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:'; /** * send input to smbclient * * @param string $input */ public function write($input) { parent::write($input . PHP_EOL); } /** * get all unprocessed output from smbclient until the next prompt * * @return string * @throws AuthenticationException * @throws ConnectException * @throws ConnectionException * @throws InvalidHostException * @throws NoLoginServerException */ public function read() { if (!$this->isValid()) { throw new ConnectionException('Connection not valid'); } $promptLine = $this->readLine(); //first line is prompt $this->checkConnectionError($promptLine); $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'); } } } $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 if the first line holds a connection failure * * @param $line * @throws AuthenticationException * @throws InvalidHostException * @throws NoLoginServerException */ private function checkConnectionError($line) { $line = rtrim($line, ')'); if (substr($line, -23) === ErrorCodes::LogonFailure) { throw new AuthenticationException('Invalid login'); } if (substr($line, -26) === ErrorCodes::BadHostName) { throw new InvalidHostException('Invalid hostname'); } if (substr($line, -22) === ErrorCodes::Unsuccessful) { throw new InvalidHostException('Connection unsuccessful'); } if (substr($line, -28) === ErrorCodes::ConnectionRefused) { throw new InvalidHostException('Connection refused'); } if (substr($line, -26) === ErrorCodes::NoLogonServers) { throw new NoLoginServerException('No login server'); } } public function close($terminate = true) { if (is_resource($this->getInputStream())) { $this->write('close' . PHP_EOL); } parent::close($terminate); } } SMB-1.0.5/src/ErrorCodes.php000066400000000000000000000017711264770370400155250ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB; class ErrorCodes { /** * connection errors */ const LogonFailure = 'NT_STATUS_LOGON_FAILURE'; 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'; const ObjectNotFound = 'NT_STATUS_OBJECT_NAME_NOT_FOUND'; const NameCollision = 'NT_STATUS_OBJECT_NAME_COLLISION'; const AccessDenied = 'NT_STATUS_ACCESS_DENIED'; const DirectoryNotEmpty = 'NT_STATUS_DIRECTORY_NOT_EMPTY'; const FileIsADirectory = 'NT_STATUS_FILE_IS_A_DIRECTORY'; const NotADirectory = 'NT_STATUS_NOT_A_DIRECTORY'; const SharingViolation = 'NT_STATUS_SHARING_VIOLATION'; } SMB-1.0.5/src/Exception/000077500000000000000000000000001264770370400146755ustar00rootroot00000000000000SMB-1.0.5/src/Exception/AccessDeniedException.php000066400000000000000000000004161264770370400216000ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Exception; class AccessDeniedException extends ConnectException {} SMB-1.0.5/src/Exception/AlreadyExistsException.php000066400000000000000000000004261264770370400220500ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Exception; class AlreadyExistsException extends InvalidRequestException {} SMB-1.0.5/src/Exception/AuthenticationException.php000066400000000000000000000004171264770370400222460ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Exception; class AuthenticationException extends ConnectException{} SMB-1.0.5/src/Exception/ConnectException.php000066400000000000000000000004021264770370400206520ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Exception; class ConnectException extends Exception {} SMB-1.0.5/src/Exception/ConnectionException.php000066400000000000000000000004141264770370400213630ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Exception; class ConnectionException extends ConnectException {} SMB-1.0.5/src/Exception/ConnectionRefusedException.php000066400000000000000000000004241264770370400227020ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Exception; class ConnectionRefusedException extends ConnectException { } SMB-1.0.5/src/Exception/Exception.php000066400000000000000000000003741264770370400173500ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Exception; class Exception extends \Exception {} SMB-1.0.5/src/Exception/FileInUseException.php000066400000000000000000000004221264770370400211060ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Exception; class FileInUseException extends InvalidRequestException {} SMB-1.0.5/src/Exception/ForbiddenException.php000066400000000000000000000004221264770370400211570ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Exception; class ForbiddenException extends InvalidRequestException {} SMB-1.0.5/src/Exception/HostDownException.php000066400000000000000000000004131264770370400210300ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Exception; class HostDownException extends ConnectException { } SMB-1.0.5/src/Exception/InvalidHostException.php000066400000000000000000000004151264770370400215110ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Exception; class InvalidHostException extends ConnectException {} SMB-1.0.5/src/Exception/InvalidPathException.php000066400000000000000000000004241264770370400214700ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Exception; class InvalidPathException extends InvalidRequestException {} SMB-1.0.5/src/Exception/InvalidRequestException.php000066400000000000000000000013041264770370400222220ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Exception; class InvalidRequestException extends Exception { /** * @var string */ protected $path; /** * @param string $path * @param int $code */ public function __construct($path, $code = 0) { $class = get_class($this); $parts = explode('\\', $class); $baseName = array_pop($parts); parent::__construct('Invalid request for ' . $path . ' (' . $baseName . ')', $code); $this->path = $path; } /** * @return string */ public function getPath() { return $this->path; } } SMB-1.0.5/src/Exception/InvalidResourceException.php000066400000000000000000000004131264770370400223610ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Exception; class InvalidResourceException extends Exception { } SMB-1.0.5/src/Exception/InvalidTypeException.php000066400000000000000000000004241264770370400215150ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Exception; class InvalidTypeException extends InvalidRequestException {} SMB-1.0.5/src/Exception/NoLoginServerException.php000066400000000000000000000004171264770370400220230ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Exception; class NoLoginServerException extends ConnectException {} SMB-1.0.5/src/Exception/NoRouteToHostException.php000066400000000000000000000004201264770370400220150ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Exception; class NoRouteToHostException extends ConnectException { } SMB-1.0.5/src/Exception/NotEmptyException.php000066400000000000000000000004211264770370400210410ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Exception; class NotEmptyException extends InvalidRequestException {} SMB-1.0.5/src/Exception/NotFoundException.php000066400000000000000000000004211264770370400210160ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Exception; class NotFoundException extends InvalidRequestException {} SMB-1.0.5/src/Exception/TimedOutException.php000066400000000000000000000004131264770370400210150ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Exception; class TimedOutException extends ConnectException { } SMB-1.0.5/src/FileInfo.php000066400000000000000000000037761264770370400151600ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB; class FileInfo implements IFileInfo { /* * Mappings of the DOS mode bits, as returned by smbc_getxattr() when the * attribute name "system.dos_attr.mode" (or "system.dos_attr.*" or * "system.*") is specified. */ const MODE_READONLY = 0x01; const MODE_HIDDEN = 0x02; const MODE_SYSTEM = 0x04; const MODE_VOLUME_ID = 0x08; const MODE_DIRECTORY = 0x10; const MODE_ARCHIVE = 0x20; const MODE_NORMAL = 0x80; /** * @var string */ protected $path; /** * @var string */ protected $name; /** * @var int */ protected $size; /** * @var int */ protected $time; /** * @var int */ protected $mode; /** * @param string $path * @param string $name * @param int $size * @param int $time * @param int $mode */ public function __construct($path, $name, $size, $time, $mode) { $this->path = $path; $this->name = $name; $this->size = $size; $this->time = $time; $this->mode = $mode; } /** * @return string */ public function getPath() { return $this->path; } /** * @return string */ public function getName() { return $this->name; } /** * @return int */ public function getSize() { return $this->size; } /** * @return int */ public function getMTime() { return $this->time; } /** * @return bool */ public function isDirectory() { return (bool)($this->mode & self::MODE_DIRECTORY); } /** * @return bool */ public function isReadOnly() { return (bool)($this->mode & self::MODE_READONLY); } /** * @return bool */ public function isHidden() { return (bool)($this->mode & self::MODE_HIDDEN); } /** * @return bool */ public function isSystem() { return (bool)($this->mode & self::MODE_SYSTEM); } /** * @return bool */ public function isArchived() { return (bool)($this->mode & self::MODE_ARCHIVE); } } SMB-1.0.5/src/IFileInfo.php000066400000000000000000000013521264770370400152550ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB; interface IFileInfo { /** * @return string */ public function getPath(); /** * @return string */ public function getName(); /** * @return int */ public function getSize(); /** * @return int */ public function getMTime(); /** * @return bool */ public function isDirectory(); /** * @return bool */ public function isReadOnly(); /** * @return bool */ public function isHidden(); /** * @return bool */ public function isSystem(); /** * @return bool */ public function isArchived(); } SMB-1.0.5/src/IShare.php000066400000000000000000000060311264770370400146230ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB; interface IShare { /** * Get the name of the share * * @return string */ public function getName(); /** * Download a remote file * * @param string $source remove file * @param string $target local file * @return bool * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function get($source, $target); /** * Upload a local file * * @param string $source local file * @param string $target remove file * @return bool * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function put($source, $target); /** * Open a readable stream top a remote file * * @param string $source * @return resource a read only stream with the contents of the remote file * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function read($source); /** * Open a writable stream to a remote file * * @param string $target * @return resource a write only stream to upload a remote file * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function write($target); /** * Rename a remote file * * @param string $from * @param string $to * @return bool * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\AlreadyExistsException */ public function rename($from, $to); /** * Delete a file on the share * * @param string $path * @return bool * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function del($path); /** * List the content of a remote folder * * @param $path * @return \Icewind\SMB\IFileInfo[] * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function dir($path); /** * @param string $path * @return \Icewind\SMB\IFileInfo * * @throws \Icewind\SMB\Exception\NotFoundException */ public function stat($path); /** * Create a folder on the share * * @param string $path * @return bool * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\AlreadyExistsException */ public function mkdir($path); /** * Remove a folder on the share * * @param string $path * @return bool * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function rmdir($path); /** * @param string $path * @param int $mode a combination of FileInfo::MODE_READONLY, FileInfo::MODE_ARCHIVE, FileInfo::MODE_SYSTEM and FileInfo::MODE_HIDDEN, FileInfo::NORMAL * @return mixed */ public function setMode($path, $mode); } SMB-1.0.5/src/NativeFileInfo.php000066400000000000000000000045001264770370400163110ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB; class NativeFileInfo implements IFileInfo { const MODE_FILE = 0100000; /** * @var string */ protected $path; /** * @var string */ protected $name; /** * @var \Icewind\SMB\NativeShare */ protected $share; /** * @var array | null */ protected $statCache; /** * @var int */ protected $modeCache; /** * @param \Icewind\SMB\NativeShare $share * @param string $path * @param string $name * @param array $stat */ public function __construct($share, $path, $name, $stat = null) { $this->share = $share; $this->path = $path; $this->name = $name; $this->statCache = $stat; } /** * @return string */ public function getPath() { return $this->path; } /** * @return string */ public function getName() { return $this->name; } /** * @return array */ protected function stat() { if (!$this->statCache) { $this->statCache = $this->share->getStat($this->getPath()); } return $this->statCache; } /** * @return int */ public function getSize() { $stat = $this->stat(); return $stat['size']; } /** * @return int */ public function getMTime() { $stat = $this->stat(); return $stat['mtime']; } /** * @return bool */ public function isDirectory() { $stat = $this->stat(); return !($stat['mode'] & self::MODE_FILE); } /** * @return int */ protected function getMode() { if (!$this->modeCache) { $attribute = $this->share->getAttribute($this->path, 'system.dos_attr.mode'); // parse hex string $this->modeCache = (int)hexdec(substr($attribute, 2)); } return $this->modeCache; } /** * @return bool */ public function isReadOnly() { $mode = $this->getMode(); return (bool)($mode & FileInfo::MODE_READONLY); } /** * @return bool */ public function isHidden() { $mode = $this->getMode(); return (bool)($mode & FileInfo::MODE_HIDDEN); } /** * @return bool */ public function isSystem() { $mode = $this->getMode(); return (bool)($mode & FileInfo::MODE_SYSTEM); } /** * @return bool */ public function isArchived() { $mode = $this->getMode(); return (bool)($mode & FileInfo::MODE_ARCHIVE); } } SMB-1.0.5/src/NativeServer.php000066400000000000000000000024161264770370400160700ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB; class NativeServer extends Server { /** * @var \Icewind\SMB\NativeState */ protected $state; /** * @param string $host * @param string $user * @param string $password */ public function __construct($host, $user, $password) { parent::__construct($host, $user, $password); $this->state = new NativeState(); } protected function connect() { $this->state->init($this->getWorkgroup(), $this->getUser(), $this->getPassword()); } /** * @return \Icewind\SMB\IShare[] * @throws \Icewind\SMB\Exception\AuthenticationException * @throws \Icewind\SMB\Exception\InvalidHostException */ public function listShares() { $this->connect(); $shares = array(); $dh = $this->state->opendir('smb://' . $this->getHost()); while ($share = $this->state->readdir($dh)) { if ($share['type'] === 'file share') { $shares[] = $this->getShare($share['name']); } } $this->state->closedir($dh); return $shares; } /** * @param string $name * @return \Icewind\SMB\IShare */ public function getShare($name) { return new NativeShare($this, $name); } } SMB-1.0.5/src/NativeShare.php000066400000000000000000000167001264770370400156650ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB; use Icewind\SMB\Exception\InvalidPathException; use Icewind\SMB\Exception\InvalidResourceException; class NativeShare extends AbstractShare { /** * @var Server $server */ private $server; /** * @var string $name */ private $name; /** * @var \Icewind\SMB\NativeState $state */ private $state; /** * @param Server $server * @param string $name */ public function __construct($server, $name) { parent::__construct(); $this->server = $server; $this->name = $name; $this->state = new NativeState(); } /** * @throws \Icewind\SMB\Exception\ConnectionException * @throws \Icewind\SMB\Exception\AuthenticationException * @throws \Icewind\SMB\Exception\InvalidHostException */ protected function connect() { if ($this->state and $this->state instanceof NativeShare) { return; } $this->state->init($this->server->getWorkgroup(), $this->server->getUser(), $this->server->getPassword()); } /** * Get the name of the share * * @return string */ public function getName() { return $this->name; } private function buildUrl($path) { $this->verifyPath($path); $url = sprintf('smb://%s/%s', $this->server->getHost(), $this->name); if ($path) { $path = trim($path, '/'); $url .= '/'; $url .= implode('/', array_map('rawurlencode', explode('/', $path))); } return $url; } /** * List the content of a remote folder * * @param string $path * @return \Icewind\SMB\IFileInfo[] * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function dir($path) { $this->connect(); $files = array(); $dh = $this->state->opendir($this->buildUrl($path)); while ($file = $this->state->readdir($dh)) { $name = $file['name']; if ($name !== '.' and $name !== '..') { $files [] = new NativeFileInfo($this, $path . '/' . $name, $name); } } $this->state->closedir($dh); return $files; } /** * @param string $path * @return \Icewind\SMB\IFileInfo[] */ public function stat($path) { return new NativeFileInfo($this, $path, basename($path), $this->getStat($path)); } public function getStat($path) { $this->connect(); return $this->state->stat($this->buildUrl($path)); } /** * Create a folder on the share * * @param string $path * @return bool * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\AlreadyExistsException */ public function mkdir($path) { $this->connect(); return $this->state->mkdir($this->buildUrl($path)); } /** * Remove a folder on the share * * @param string $path * @return bool * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function rmdir($path) { $this->connect(); return $this->state->rmdir($this->buildUrl($path)); } /** * Delete a file on the share * * @param string $path * @return bool * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function del($path) { $this->connect(); return $this->state->unlink($this->buildUrl($path)); } /** * Rename a remote file * * @param string $from * @param string $to * @return bool * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\AlreadyExistsException */ public function rename($from, $to) { $this->connect(); return $this->state->rename($this->buildUrl($from), $this->buildUrl($to)); } /** * Upload a local file * * @param string $source local file * @param string $target remove file * @return bool * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function put($source, $target) { $this->connect(); $sourceHandle = fopen($source, 'rb'); $targetHandle = $this->state->create($this->buildUrl($target)); while ($data = fread($sourceHandle, 4096)) { $this->state->write($targetHandle, $data); } $this->state->close($targetHandle); return true; } /** * Download a remote file * * @param string $source remove file * @param string $target local file * @return bool * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException * @throws \Icewind\SMB\Exception\InvalidPathException * @throws \Icewind\SMB\Exception\InvalidResourceException */ public function get($source, $target) { if (!$target) { throw new InvalidPathException('Invalid target path: Filename cannot be empty'); } $targetHandle = @fopen($target, 'wb'); if (!$targetHandle) { $error = error_get_last(); if (is_array($error)) { $reason = $error['message']; } else { $reason = 'Unknown error'; } throw new InvalidResourceException('Failed opening local file "' . $target . '" for writing: ' . $reason); } $this->connect(); $sourceHandle = $this->state->open($this->buildUrl($source), 'r'); if (!$sourceHandle) { fclose($targetHandle); throw new InvalidResourceException('Failed opening remote file "' . $source . '" for reading'); } while ($data = $this->state->read($sourceHandle, 4096)) { fwrite($targetHandle, $data); } $this->state->close($sourceHandle); return true; } /** * Open a readable stream top a remote file * * @param string $source * @return resource a read only stream with the contents of the remote file * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function read($source) { $this->connect(); $handle = $this->state->open($this->buildUrl($source), 'r'); return NativeStream::wrap($this->state, $handle, 'r'); } /** * Open a readable stream top a remote file * * @param string $source * @return resource a read only stream with the contents of the remote file * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function write($source) { $this->connect(); $handle = $this->state->create($this->buildUrl($source)); return NativeStream::wrap($this->state, $handle, 'w'); } /** * Get extended attributes for the path * * @param string $path * @param string $attribute attribute to get the info * @return string the attribute value */ public function getAttribute($path, $attribute) { $this->connect(); $result = $this->state->getxattr($this->buildUrl($path), $attribute); return $result; } /** * Get extended attributes for the path * * @param string $path * @param string $attribute attribute to get the info * @param mixed $value * @return string the attribute value */ public function setAttribute($path, $attribute, $value) { $this->connect(); if ($attribute === 'system.dos_attr.mode' and is_int($value)) { $value = '0x' . dechex($value); } return $this->state->setxattr($this->buildUrl($path), $attribute, $value); } /** * @param string $path * @param int $mode a combination of FileInfo::MODE_READONLY, FileInfo::MODE_ARCHIVE, FileInfo::MODE_SYSTEM and FileInfo::MODE_HIDDEN, FileInfo::NORMAL * @return mixed */ public function setMode($path, $mode) { return $this->setAttribute($path, 'system.dos_attr.mode', $mode); } public function __destruct() { unset($this->state); } } SMB-1.0.5/src/NativeState.php000066400000000000000000000150171264770370400157030ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB; use Icewind\SMB\Exception\AlreadyExistsException; use Icewind\SMB\Exception\ConnectionRefusedException; use Icewind\SMB\Exception\Exception; use Icewind\SMB\Exception\ForbiddenException; use Icewind\SMB\Exception\HostDownException; use Icewind\SMB\Exception\InvalidTypeException; use Icewind\SMB\Exception\NoRouteToHostException; use Icewind\SMB\Exception\NotEmptyException; use Icewind\SMB\Exception\NotFoundException; use Icewind\SMB\Exception\TimedOutException; /** * Low level wrapper for libsmbclient-php for error handling */ class NativeState { /** * @var resource */ protected $state; protected $handlerSet = false; protected $connected = false; protected function handleError($path) { $error = smbclient_state_errno($this->state); switch ($error) { // see error.h case 0; return; case 1: case 13: throw new ForbiddenException($path, $error); case 2: throw new NotFoundException($path, $error); case 17: throw new AlreadyExistsException($path, $error); case 20: throw new InvalidTypeException($path, $error); case 21: throw new InvalidTypeException($path, $error); case 39: throw new NotEmptyException($path, $error); case 110: throw new TimedOutException($path, $error); case 111: throw new ConnectionRefusedException($path, $error); case 112: throw new HostDownException($path, $error); case 113: throw new NoRouteToHostException($path, $error); default: $message = 'Unknown error (' . $error . ')'; if ($path) { $message .= ' for ' . $path; } throw new Exception($message, $error); } } protected function testResult($result, $path) { if ($result === false or $result === null) { $this->handleError($path); } } /** * @param string $workGroup * @param string $user * @param string $password * @return bool */ public function init($workGroup, $user, $password) { if ($this->connected) { return true; } $this->state = smbclient_state_new(); $result = @smbclient_state_init($this->state, $workGroup, $user, $password); $this->testResult($result, ''); $this->connected = true; return $result; } /** * @param string $uri * @return resource */ public function opendir($uri) { $result = @smbclient_opendir($this->state, $uri); $this->testResult($result, $uri); return $result; } /** * @param resource $dir * @return array */ public function readdir($dir) { $result = @smbclient_readdir($this->state, $dir); $this->testResult($result, $dir); return $result; } /** * @param $dir * @return bool */ public function closedir($dir) { $result = smbclient_closedir($this->state, $dir); $this->testResult($result, $dir); return $result; } /** * @param string $old * @param string $new * @return bool */ public function rename($old, $new) { $result = @smbclient_rename($this->state, $old, $this->state, $new); $this->testResult($result, $new); return $result; } /** * @param string $uri * @return bool */ public function unlink($uri) { $result = @smbclient_unlink($this->state, $uri); $this->testResult($result, $uri); return $result; } /** * @param string $uri * @param int $mask * @return bool */ public function mkdir($uri, $mask = 0777) { $result = @smbclient_mkdir($this->state, $uri, $mask); $this->testResult($result, $uri); return $result; } /** * @param string $uri * @return bool */ public function rmdir($uri) { $result = @smbclient_rmdir($this->state, $uri); $this->testResult($result, $uri); return $result; } /** * @param string $uri * @return array */ public function stat($uri) { $result = @smbclient_stat($this->state, $uri); $this->testResult($result, $uri); return $result; } /** * @param resource $file * @return array */ public function fstat($file) { $result = @smbclient_fstat($this->state, $file); $this->testResult($result, $file); return $result; } /** * @param string $uri * @param string $mode * @param int $mask * @return resource */ public function open($uri, $mode, $mask = 0666) { $result = @smbclient_open($this->state, $uri, $mode, $mask); $this->testResult($result, $uri); return $result; } /** * @param string $uri * @param int $mask * @return resource */ public function create($uri, $mask = 0666) { $result = @smbclient_creat($this->state, $uri, $mask); $this->testResult($result, $uri); return $result; } /** * @param resource $file * @param int $bytes * @return string */ public function read($file, $bytes) { $result = @smbclient_read($this->state, $file, $bytes); $this->testResult($result, $file); return $result; } /** * @param resource $file * @param string $data * @param int $length * @return int */ public function write($file, $data, $length = null) { $result = @smbclient_write($this->state, $file, $data, $length); $this->testResult($result, $file); return $result; } /** * @param resource $file * @param int $offset * @param int $whence SEEK_SET | SEEK_CUR | SEEK_END * @return int | bool new file offset as measured from the start of the file on success, false on failure. */ public function lseek($file, $offset, $whence = SEEK_SET) { $result = @smbclient_lseek($this->state, $file, $offset, $whence); $this->testResult($result, $file); return $result; } /** * @param resource $file * @param int $size * @return bool */ public function ftruncate($file, $size) { $result = @smbclient_ftruncate($this->state, $file, $size); $this->testResult($result, $file); return $result; } public function close($file) { $result = @smbclient_close($this->state, $file); $this->testResult($result, $file); return $result; } /** * @param string $uri * @param string $key * @return string */ public function getxattr($uri, $key) { $result = @smbclient_getxattr($this->state, $uri, $key); $this->testResult($result, $uri); return $result; } /** * @param string $uri * @param string $key * @param string $value * @param int $flags * @return mixed */ public function setxattr($uri, $key, $value, $flags = 0) { $result = @smbclient_setxattr($this->state, $uri, $key, $value, $flags); $this->testResult($result, $uri); return $result; } public function __destruct() { if ($this->connected) { smbclient_state_free($this->state); } } } SMB-1.0.5/src/NativeStream.php000066400000000000000000000046111264770370400160540ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB; use Icewind\SMB\Exception\InvalidRequestException; use Icewind\Streams\File; class NativeStream implements File { /** * @var resource */ public $context; /** * @var \Icewind\SMB\NativeState */ private $state; /** * @var resource */ private $handle; /** * @var bool */ private $eof = false; /** * Wrap a stream from libsmbclient-php into a regular php stream * * @param \Icewind\SMB\NativeState $state * @param resource $smbStream * @param string $mode * @return resource */ public static function wrap($state, $smbStream, $mode) { stream_wrapper_register('nativesmb', '\Icewind\SMB\NativeStream'); $context = stream_context_create(array( 'nativesmb' => array( 'state' => $state, 'handle' => $smbStream ) )); $fh = fopen('nativesmb://', $mode, false, $context); stream_wrapper_unregister('nativesmb'); return $fh; } public function stream_close() { return $this->state->close($this->handle); } public function stream_eof() { return $this->eof; } public function stream_flush() { } public function stream_open($path, $mode, $options, &$opened_path) { $context = stream_context_get_options($this->context); $this->state = $context['nativesmb']['state']; $this->handle = $context['nativesmb']['handle']; return true; } public function stream_read($count) { $result = $this->state->read($this->handle, $count); if (strlen($result) < $count) { $this->eof = true; } return $result; } public function stream_seek($offset, $whence = SEEK_SET) { $this->eof = false; try { return $this->state->lseek($this->handle, $offset, $whence) !== false; } catch (InvalidRequestException $e) { return false; } } public function stream_stat() { return $this->state->fstat($this->handle); } public function stream_tell() { return $this->state->lseek($this->handle, 0, SEEK_CUR); } public function stream_write($data) { return $this->state->write($this->handle, $data); } public function stream_truncate($size) { return $this->state->ftruncate($this->handle, $size); } public function stream_set_option($option, $arg1, $arg2) { return false; } public function stream_lock($operation) { return false; } } SMB-1.0.5/src/Parser.php000066400000000000000000000100041264770370400146770ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB; use Icewind\SMB\Exception\AccessDeniedException; use Icewind\SMB\Exception\AlreadyExistsException; use Icewind\SMB\Exception\Exception; use Icewind\SMB\Exception\FileInUseException; use Icewind\SMB\Exception\InvalidResourceException; use Icewind\SMB\Exception\InvalidTypeException; use Icewind\SMB\Exception\NotEmptyException; use Icewind\SMB\Exception\NotFoundException; class Parser { /** * @var \Icewind\SMB\TimeZoneProvider */ protected $timeZoneProvider; /** * @param \Icewind\SMB\TimeZoneProvider $timeZoneProvider */ public function __construct(TimeZoneProvider $timeZoneProvider) { $this->timeZoneProvider = $timeZoneProvider; } public function checkForError($output, $path) { if (count($output) === 0) { return true; } else { if (strpos($output[0], 'does not exist')) { throw new NotFoundException($path); } $parts = explode(' ', $output[0]); $error = false; foreach ($parts as $part) { if (substr($part, 0, 9) === 'NT_STATUS') { $error = $part; } } $notFoundMsg = 'Error opening local file '; if (substr($output[0], 0, strlen($notFoundMsg)) === $notFoundMsg) { $localPath = substr($output[0], strlen($notFoundMsg)); throw new InvalidResourceException('Failed opening local file "' . $localPath . '" for writing'); } switch ($error) { case ErrorCodes::PathNotFound: case ErrorCodes::ObjectNotFound: case ErrorCodes::NoSuchFile: throw new NotFoundException($path); case ErrorCodes::NameCollision: throw new AlreadyExistsException($path); case ErrorCodes::AccessDenied: throw new AccessDeniedException($path); case ErrorCodes::DirectoryNotEmpty: throw new NotEmptyException($path); case ErrorCodes::FileIsADirectory: case ErrorCodes::NotADirectory: throw new InvalidTypeException($path); case ErrorCodes::SharingViolation: throw new FileInUseException($path); default: $message = 'Unknown error (' . $error . ')'; if ($path) { $message .= ' for ' . $path; } throw new Exception($message); } } } public function parseMode($mode) { $result = 0; $modeStrings = array( 'R' => FileInfo::MODE_READONLY, 'H' => FileInfo::MODE_HIDDEN, 'S' => FileInfo::MODE_SYSTEM, 'D' => FileInfo::MODE_DIRECTORY, 'A' => FileInfo::MODE_ARCHIVE, 'N' => FileInfo::MODE_NORMAL ); foreach ($modeStrings as $char => $val) { if (strpos($mode, $char) !== false) { $result |= $val; } } return $result; } public function parseStat($output) { $mtime = 0; $mode = 0; $size = 0; foreach ($output as $line) { // A line = explode statement may not fill all array elements // properly. May happen when accessing non Windows Fileservers $words = explode(':', $line, 2); $name = isset($words[0]) ? $words[0] : ''; $value = isset($words[1]) ? $words[1] : ''; $value = trim($value); if ($name === 'write_time') { $mtime = strtotime($value); } else if ($name === 'attributes') { $mode = hexdec(substr($value, 1, -1)); } else if ($name === 'stream') { list(, $size,) = explode(' ', $value); $size = intval($size); } } return array( 'mtime' => $mtime, 'mode' => $mode, 'size' => $size ); } public function parseDir($output, $basePath) { //last line is used space array_pop($output); $regex = '/^\s*(.*?)\s\s\s\s+(?:([NDHARS]*)\s+)?([0-9]+)\s+(.*)$/'; //2 spaces, filename, optional type, size, date $content = array(); foreach ($output as $line) { if (preg_match($regex, $line, $matches)) { list(, $name, $mode, $size, $time) = $matches; if ($name !== '.' and $name !== '..') { $mode = $this->parseMode($mode); $time = strtotime($time . ' ' . $this->timeZoneProvider->get()); $content[] = new FileInfo($basePath . '/' . $name, $name, $size, $time, $mode); } } } return $content; } } SMB-1.0.5/src/RawConnection.php000066400000000000000000000075101264770370400162240ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB; use Icewind\SMB\Exception\ConnectionException; class RawConnection { /** * @var string */ private $command; /** * @var string[] */ private $env; /** * @var resource[] $pipes * * $pipes[0] holds STDIN for smbclient * $pipes[1] holds STDOUT for smbclient */ private $pipes; /** * @var resource $process */ private $process; public function __construct($command, $env = array()) { $this->command = $command; $this->env = $env; $this->connect(); } private function connect() { $descriptorSpec = array( 0 => array('pipe', 'r'), // child reads from stdin 1 => array('pipe', 'w'), // child writes to stdout 2 => array('pipe', 'w'), // child writes to stderr 3 => array('pipe', 'r'), // child reads from fd#3 4 => array('pipe', 'r'), // child reads from fd#4 5 => array('pipe', 'w') // child writes to fd#5 ); setlocale(LC_ALL, Server::LOCALE); $env = array_merge($this->env, array( 'CLI_FORCE_INTERACTIVE' => 'y', // Needed or the prompt isn't displayed!! 'LC_ALL' => Server::LOCALE, 'LANG' => Server::LOCALE, 'COLUMNS' => 8192 // prevent smbclient from line-wrapping it's output )); $this->process = proc_open($this->command, $descriptorSpec, $this->pipes, '/', $env); if (!$this->isValid()) { throw new ConnectionException(); } } /** * check if the connection is still active * * @return bool */ public function isValid() { if (is_resource($this->process)) { $status = proc_get_status($this->process); return $status['running']; } else { return false; } } /** * send input to the process * * @param string $input */ public function write($input) { fwrite($this->getInputStream(), $input); fflush($this->getInputStream()); } /** * read a line of output * * @return string */ public function readLine() { return stream_get_line($this->getOutputStream(), 4086, "\n"); } /** * read a line of output * * @return string */ public function readError() { return trim(stream_get_line($this->getErrorStream(), 4086)); } /** * get all output until the process closes * * @return array */ public function readAll() { $output = array(); while ($line = $this->readLine()) { $output[] = $line; } return $output; } public function getInputStream() { return $this->pipes[0]; } public function getOutputStream() { return $this->pipes[1]; } public function getErrorStream() { return $this->pipes[2]; } public function getAuthStream() { return $this->pipes[3]; } public function getFileInputStream() { return $this->pipes[4]; } public function getFileOutputStream() { return $this->pipes[5]; } public function writeAuthentication($user, $password) { $auth = ($password === false) ? "username=$user" : "username=$user\npassword=$password"; if (fwrite($this->getAuthStream(), $auth) === false) { fclose($this->getAuthStream()); return false; } fclose($this->getAuthStream()); return true; } public function close($terminate = true) { if (!is_resource($this->process)) { return; } if ($terminate) { // if for case that posix_ functions are not available if (function_exists('posix_kill')) { $status = proc_get_status($this->process); $ppid = $status['pid']; $pids = preg_split('/\s+/', `ps -o pid --no-heading --ppid $ppid`); foreach($pids as $pid) { if(is_numeric($pid)) { //9 is the SIGKILL signal posix_kill($pid, 9); } } } proc_terminate($this->process); } proc_close($this->process); } public function reconnect() { $this->close(); $this->connect(); } public function __destruct() { $this->close(); } } SMB-1.0.5/src/Server.php000066400000000000000000000073111264770370400147200ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB; use Icewind\SMB\Exception\AuthenticationException; use Icewind\SMB\Exception\InvalidHostException; class Server { const LOCALE = 'en_US.UTF-8'; /** * @var string $host */ protected $host; /** * @var string $user */ protected $user; /** * @var string $password */ protected $password; /** * @var string $workgroup */ protected $workgroup; /** * @var \Icewind\SMB\System */ private $system; /** * @var TimeZoneProvider */ private $timezoneProvider; /** * Check if the smbclient php extension is available * * @return bool */ public static function NativeAvailable() { return function_exists('smbclient_state_new'); } /** * @param string $host * @param string $user * @param string $password */ public function __construct($host, $user, $password) { $this->host = $host; list($workgroup, $user) = $this->splitUser($user); $this->user = $user; $this->workgroup = $workgroup; $this->password = $password; $this->system = new System(); $this->timezoneProvider = new TimeZoneProvider($host, $this->system); } /** * 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 */ public function getAuthString() { return $this->user . '%' . $this->password; } /** * @return string */ public function getUser() { return $this->user; } /** * @return string */ public function getPassword() { return $this->password; } /** * return string */ public function getHost() { return $this->host; } /** * @return string */ public function getWorkgroup() { return $this->workgroup; } /** * @return \Icewind\SMB\IShare[] * * @throws \Icewind\SMB\Exception\AuthenticationException * @throws \Icewind\SMB\Exception\InvalidHostException */ public function listShares() { $workgroupArgument = ($this->workgroup) ? ' -W ' . escapeshellarg($this->workgroup) : ''; $command = sprintf('%s %s --authentication-file=%s -gL %s', $this->system->getSmbclientPath(), $workgroupArgument, System::getFD(3), escapeshellarg($this->getHost()) ); $connection = new RawConnection($command); $connection->writeAuthentication($this->getUser(), $this->getPassword()); $output = $connection->readAll(); $line = $output[0]; $line = rtrim($line, ')'); if (substr($line, -23) === ErrorCodes::LogonFailure) { throw new AuthenticationException(); } if (substr($line, -26) === ErrorCodes::BadHostName) { throw new InvalidHostException(); } if (substr($line, -22) === ErrorCodes::Unsuccessful) { throw new InvalidHostException(); } if (substr($line, -28) === ErrorCodes::ConnectionRefused) { throw new InvalidHostException(); } $shareNames = array(); foreach ($output as $line) { if (strpos($line, '|')) { list($type, $name, $description) = explode('|', $line); if (strtolower($type) === 'disk') { $shareNames[$name] = $description; } } } $shares = array(); foreach ($shareNames as $name => $description) { $shares[] = $this->getShare($name); } return $shares; } /** * @param string $name * @return \Icewind\SMB\IShare */ public function getShare($name) { return new Share($this, $name); } /** * @return string */ public function getTimeZone() { return $this->timezoneProvider->get(); } } SMB-1.0.5/src/Share.php000066400000000000000000000265151264770370400145230ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB; use Icewind\SMB\Exception\ConnectionException; use Icewind\SMB\Exception\FileInUseException; use Icewind\SMB\Exception\InvalidTypeException; use Icewind\SMB\Exception\NotFoundException; use Icewind\Streams\CallbackWrapper; class Share extends AbstractShare { /** * @var Server $server */ private $server; /** * @var string $name */ private $name; /** * @var Connection $connection */ public $connection; /** * @var \Icewind\SMB\Parser */ protected $parser; /** * @var \Icewind\SMB\System */ private $system; /** * @param Server $server * @param string $name */ public function __construct($server, $name) { parent::__construct(); $this->server = $server; $this->name = $name; $this->system = new System(); $this->parser = new Parser(new TimeZoneProvider($this->server->getHost(), $this->system)); } /** * @throws \Icewind\SMB\Exception\ConnectionException * @throws \Icewind\SMB\Exception\AuthenticationException * @throws \Icewind\SMB\Exception\InvalidHostException */ protected function connect() { 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(); } } protected function reconnect() { $this->connection->reconnect(); $this->connection->writeAuthentication($this->server->getUser(), $this->server->getPassword()); if (!$this->connection->isValid()) { throw new ConnectionException(); } } /** * Get the name of the share * * @return string */ public function getName() { return $this->name; } protected function simpleCommand($command, $path) { $path = $this->escapePath($path); $cmd = $command . ' ' . $path; $output = $this->execute($cmd); return $this->parseOutput($output, $path); } /** * List the content of a remote folder * * @param $path * @return \Icewind\SMB\IFileInfo[] * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function dir($path) { $escapedPath = $this->escapePath($path); $output = $this->execute('cd ' . $escapedPath); //check output for errors $this->parseOutput($output, $path); $output = $this->execute('dir'); $this->execute('cd /'); return $this->parser->parseDir($output, $path); } /** * @param string $path * @return \Icewind\SMB\IFileInfo[] */ public function stat($path) { $escapedPath = $this->escapePath($path); $output = $this->execute('allinfo ' . $escapedPath); // Windows and non Windows Fileserver may respond different // to the allinfo command for directories. If the result is a single // line = error line, redo it with a different allinfo parameter if ($escapedPath == '""' && count($output) < 2) { $output = $this->execute('allinfo ' . '"."'); } if (count($output) < 3) { $this->parseOutput($output, $path); } $stat = $this->parser->parseStat($output); return new FileInfo($path, basename($path), $stat['size'], $stat['mtime'], $stat['mode']); } /** * Create a folder on the share * * @param string $path * @return bool * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\AlreadyExistsException */ public function mkdir($path) { return $this->simpleCommand('mkdir', $path); } /** * Remove a folder on the share * * @param string $path * @return bool * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function rmdir($path) { return $this->simpleCommand('rmdir', $path); } /** * Delete a file on the share * * @param string $path * @param bool $secondTry * @return bool * @throws InvalidTypeException * @throws NotFoundException * @throws \Exception */ public function del($path, $secondTry = false) { //del return a file not found error when trying to delete a folder //we catch it so we can check if $path doesn't exist or is of invalid type try { return $this->simpleCommand('del', $path); } catch (NotFoundException $e) { //no need to do anything with the result, we just check if this throws the not found error try { $this->simpleCommand('ls', $path); } catch (NotFoundException $e2) { throw $e; } catch (\Exception $e2) { throw new InvalidTypeException($path); } throw $e; } catch (FileInUseException $e) { if ($secondTry) { throw $e; } $this->reconnect(); return $this->del($path, true); } } /** * Rename a remote file * * @param string $from * @param string $to * @return bool * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\AlreadyExistsException */ public function rename($from, $to) { $path1 = $this->escapePath($from); $path2 = $this->escapePath($to); $cmd = 'rename ' . $path1 . ' ' . $path2; $output = $this->execute($cmd); return $this->parseOutput($output, $to); } /** * Upload a local file * * @param string $source local file * @param string $target remove file * @return bool * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function put($source, $target) { $path1 = $this->escapeLocalPath($source); //first path is local, needs different escaping $path2 = $this->escapePath($target); $output = $this->execute('put ' . $path1 . ' ' . $path2); return $this->parseOutput($output, $target); } /** * Download a remote file * * @param string $source remove file * @param string $target local file * @return bool * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function get($source, $target) { $path1 = $this->escapePath($source); $path2 = $this->escapeLocalPath($target); //second path is local, needs different escaping $output = $this->execute('get ' . $path1 . ' ' . $path2); return $this->parseOutput($output, $source); } /** * Open a readable stream to a remote file * * @param string $source * @return resource a read only stream with the contents of the remote file * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function read($source) { $source = $this->escapePath($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 $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) ); $connection = new Connection($command); $connection->writeAuthentication($this->server->getUser(), $this->server->getPassword()); $connection->write('get ' . $source . ' ' . System::getFD(5)); $connection->write('exit'); $fh = $connection->getFileOutputStream(); stream_context_set_option($fh, 'file', 'connection', $connection); return $fh; } /** * Open a writable stream to a remote file * * @param string $target * @return resource a write only stream to upload a remote file * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function write($target) { $target = $this->escapePath($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 $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) ); $connection = new Connection($command); $connection->writeAuthentication($this->server->getUser(), $this->server->getPassword()); $fh = $connection->getFileInputStream(); $connection->write('put ' . System::getFD(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, $target) { $connection->close(false); // dont terminate, give the upload some time }); } /** * @param string $path * @param int $mode a combination of FileInfo::MODE_READONLY, FileInfo::MODE_ARCHIVE, FileInfo::MODE_SYSTEM and FileInfo::MODE_HIDDEN, FileInfo::NORMAL * @return mixed */ public function setMode($path, $mode) { $modeString = ''; $modeMap = array( FileInfo::MODE_READONLY => 'r', FileInfo::MODE_HIDDEN => 'h', FileInfo::MODE_ARCHIVE => 'a', FileInfo::MODE_SYSTEM => 's' ); foreach ($modeMap as $modeByte => $string) { if ($mode & $modeByte) { $modeString .= $string; } } $path = $this->escapePath($path); // first reset the mode to normal $cmd = 'setmode ' . $path . ' -rsha'; $output = $this->execute($cmd); $this->parseOutput($output, $path); // then set the modes we want $cmd = 'setmode ' . $path . ' ' . $modeString; $output = $this->execute($cmd); return $this->parseOutput($output, $path); } /** * @param string $command * @return array */ protected function execute($command) { $this->connect(); $this->connection->write($command . PHP_EOL); $output = $this->connection->read(); return $output; } /** * check output for errors * * @param string[] $lines * @param string $path * * @throws NotFoundException * @throws \Icewind\SMB\Exception\AlreadyExistsException * @throws \Icewind\SMB\Exception\AccessDeniedException * @throws \Icewind\SMB\Exception\NotEmptyException * @throws \Icewind\SMB\Exception\InvalidTypeException * @throws \Icewind\SMB\Exception\Exception * @return bool */ protected function parseOutput($lines, $path = '') { $this->parser->checkForError($lines, $path); } /** * @param string $string * @return string */ protected function escape($string) { return escapeshellarg($string); } /** * @param string $path * @return string */ protected function escapePath($path) { $this->verifyPath($path); if ($path === '/') { $path = ''; } $path = str_replace('/', '\\', $path); $path = str_replace('"', '^"', $path); return '"' . $path . '"'; } /** * @param string $path * @return string */ protected function escapeLocalPath($path) { $path = str_replace('"', '\"', $path); return '"' . $path . '"'; } public function __destruct() { unset($this->connection); } } SMB-1.0.5/src/System.php000066400000000000000000000014701264770370400147360ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB; use Icewind\SMB\Exception\Exception; class System { private $smbclient; private $net; public static function getFD($num) { $folders = array( '/proc/self/fd', '/dev/fd' ); foreach ($folders as $folder) { if (file_exists($folder)) { return $folder . '/' . $num; } } throw new Exception('Cant find file descriptor path'); } public function getSmbclientPath() { if (!$this->smbclient) { $this->smbclient = trim(`which smbclient`); } return $this->smbclient; } public function getNetPath() { if (!$this->net) { $this->net = trim(`which net`); } return $this->net; } } SMB-1.0.5/src/TimeZoneProvider.php000066400000000000000000000016021264770370400167140ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB; class TimeZoneProvider { /** * @var string */ private $host; /** * @var string */ private $timeZone; /** * @var System */ private $system; /** * @param string $host * @param System $system */ function __construct($host, System $system) { $this->host = $host; $this->system = $system; } public function get() { if (!$this->timeZone) { $net = $this->system->getNetPath(); if ($net) { $command = sprintf('%s time zone -S %s', $net, escapeshellarg($this->host) ); $this->timeZone = exec($command); } else { // fallback to server timezone $this->timeZone = date_default_timezone_get(); } } return $this->timeZone; } } SMB-1.0.5/tests/000077500000000000000000000000001264770370400133125ustar00rootroot00000000000000SMB-1.0.5/tests/AbstractShare.php000066400000000000000000000440371264770370400165610ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Test; use Icewind\SMB\Exception\InvalidPathException; use Icewind\SMB\FileInfo; abstract class AbstractShare extends 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() { return array( array('simple'), array('with spaces_and-underscores'), array("single'quote'"), array("foo ; asd -- bar"), 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 invalidPathProvider() { // / ? < > \ : * | " are illegal characters in path on windows return array( array("new\nline"), array("\rreturn"), array('null' . chr(0) . 'byte'), array('foo?bar'), array('foo'), array('foo:bar'), array('foo*bar'), array('foo|bar'), array('foo"bar"') ); } 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 invalidPathProvider * @expectedException \Icewind\SMB\Exception\InvalidPathException */ public function testMkdirInvalidPath($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 invalidPathProvider * @expectedException \Icewind\SMB\Exception\InvalidPathException */ public function testPutInvalidPath($name) { $tmpFile = $this->getTextFile('foo'); try { $this->share->put($tmpFile, $this->root . '/' . $name); } catch (InvalidPathException $e) { unlink($tmpFile); throw $e; } unlink($tmpFile); } /** * @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); } /** * @expectedException \Icewind\SMB\Exception\InvalidResourceException */ public function testGetInvalidTarget() { $name = 'test.txt'; $text = 'dummy'; $tmpFile = $this->getTextFile($text); $this->share->put($tmpFile, $this->root . '/' . $name); unlink($tmpFile); $this->share->get($this->root . '/' . $name, '/non/existing/file'); } /** * @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'); } /** * @dataProvider invalidPathProvider * @expectedException \Icewind\SMB\Exception\InvalidPathException */ public function testDownloadInvalidPath($name) { $this->share->get($name, ''); } /** * @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'); } /** * @dataProvider invalidPathProvider * @expectedException \Icewind\SMB\Exception\InvalidPathException */ public function testDelInvalidPath($name) { $this->share->del($name); } /** * @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'); } /** * @dataProvider invalidPathProvider * @expectedException \Icewind\SMB\Exception\InvalidPathException */ public function testRmDirInvalidPath($name) { $this->share->rmdir($name); } /** * @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'); } /** * @dataProvider invalidPathProvider * @expectedException \Icewind\SMB\Exception\InvalidPathException */ public function testRenameInvalidPath($name) { $this->share->rename($name, $name . '_'); } /** * @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 invalidPathProvider * @expectedException \Icewind\SMB\Exception\InvalidPathException */ public function testReadStreamInvalidPath($name) { $this->share->read($name); } /** * @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); } /** * @dataProvider invalidPathProvider * @expectedException \Icewind\SMB\Exception\InvalidPathException */ public function testWriteStreamInvalidPath($name) { $fh = $this->share->write($this->root . '/' . $name); fwrite($fh, 'foo'); fclose($fh); } 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 invalidPathProvider * @expectedException \Icewind\SMB\Exception\InvalidPathException */ public function testDirInvalidPath($name) { $this->share->dir($name); } /** * @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()); } /** * @dataProvider invalidPathProvider * @expectedException \Icewind\SMB\Exception\InvalidPathException */ public function testStatInvalidPath($name) { $this->share->stat($name); } /** * @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); } } SMB-1.0.5/tests/NativeShare.php000066400000000000000000000016031264770370400162340ustar00rootroot00000000000000 * 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() { $this->requireBackendEnv('libsmbclient'); 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); } } SMB-1.0.5/tests/NativeStream.php000066400000000000000000000073411264770370400164320ustar00rootroot00000000000000 * 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 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() { $this->requireBackendEnv('libsmbclient'); 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); } } SMB-1.0.5/tests/Parser.php000066400000000000000000000055071264770370400152660ustar00rootroot00000000000000 * 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, '')); } } SMB-1.0.5/tests/Server.php000066400000000000000000000034761264770370400153030ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Test; class Server extends TestCase { /** * @var \Icewind\SMB\Server $server */ private $server; private $config; public function setUp() { $this->requireBackendEnv('smbclient'); $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(); } /** * @expectedException \Icewind\SMB\Exception\InvalidHostException */ public function testHostEscape() { $server = new \Icewind\SMB\Server($this->config->host . ';asd', $this->config->user, $this->config->password); $server->listShares(); } } SMB-1.0.5/tests/Share.php000066400000000000000000000022701264770370400150660ustar00rootroot00000000000000 * 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->requireBackendEnv('smbclient'); $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); } /** * @expectedException \Icewind\SMB\Exception\InvalidHostException */ public function testHostEscape() { $this->requireBackendEnv('smbclient'); $this->config = json_decode(file_get_contents(__DIR__ . '/config.json')); $this->server = new NormalServer($this->config->host . ';asd', $this->config->user, $this->config->password); $share = $this->server->getShare($this->config->share); $share->dir($this->root); } } SMB-1.0.5/tests/TestCase.php000066400000000000000000000007221264770370400155370ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ namespace Icewind\SMB\Test; abstract class TestCase extends \PHPUnit_Framework_TestCase { protected function requireBackendEnv($backend) { if (getenv('BACKEND') and getenv('BACKEND') !== $backend) { $this->markTestSkipped('Skipping tests for ' . $backend . ' backend'); } } } SMB-1.0.5/tests/bootstrap.php000066400000000000000000000004061264770370400160400ustar00rootroot00000000000000 * This file is licensed under the Licensed under the MIT license: * http://opensource.org/licenses/MIT */ date_default_timezone_set('UTC'); require_once __DIR__.'/../vendor/autoload.php'; SMB-1.0.5/tests/config.json000066400000000000000000000001421264770370400154470ustar00rootroot00000000000000{ "host": "localhost", "user": "test", "password": "test", "share": "test", "root": "test" } SMB-1.0.5/tests/phpunit.xml000066400000000000000000000002471264770370400155260ustar00rootroot00000000000000 ./