initial commit

This commit is contained in:
Robin Appelman 2012-12-10 00:18:54 +01:00
commit e324e70050
21 changed files with 1410 additions and 0 deletions

19
src/autoload.php Normal file
View file

@ -0,0 +1,19 @@
<?php
/**
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
set_include_path(get_include_path() . PATH_SEPARATOR . __DIR__);
require_once 'errors.php';
spl_autoload_register(function ($class) {
if (substr($class, 0, 4) == 'SMB\\') {
$class = strtolower($class);
$file = str_replace('\\', '/', substr($class, 4));
include $file . '.php';
}
});

61
src/command/command.php Normal file
View file

@ -0,0 +1,61 @@
<?php
/**
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace SMB\Command;
abstract class Command {
const CLIENT = 'smbclient';
/**
* @var \SMB\Connection $connection
*/
protected $connection;
/**
* @param \SMB\Connection $connection
*/
public function __construct($connection) {
$this->connection = $connection;
}
/**
* @param string $command
* @return array
*/
protected function execute($command) {
$auth = $this->escape($this->connection->getAuthString());
$command = self::CLIENT . ' -N -U ' . $auth . ' ' . $command . ' 2> /dev/null';
exec($command, $output);
return $output;
}
abstract public function run($arguments);
/**
* @param array $lines
* @return mixed
*/
abstract protected function parseOutput($lines);
/**
* @param string $string
* @return string
*/
public function escape($string) {
return escapeshellarg($string);
}
/**
* @param string $path
* @return string
*/
public function escapePath($path) {
$path = str_replace('/', '\\', $path);
return '"' . trim(escapeshellarg($path), "'") . '"';
}
}

36
src/command/del.php Normal file
View file

@ -0,0 +1,36 @@
<?php
/**
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace SMB\Command;
class Del extends Simple {
public function __construct($connection) {
parent::__construct($connection);
$this->command = 'del';
}
/**
* @param $lines
* @return bool
*/
protected function parseOutput($lines) {
if (count($lines) === 0) {
return true;
} else {
list($error,) = explode(' ', $lines[0]);
switch ($error) {
case 'NT_STATUS_OBJECT_PATH_NOT_FOUND':
case 'NT_STATUS_OBJECT_NAME_NOT_FOUND':
case 'NT_STATUS_NO_SUCH_FILE':
throw new \SMB\NotFoundException();
default:
throw new \Exception();
}
}
}
}

47
src/command/dir.php Normal file
View file

@ -0,0 +1,47 @@
<?php
/**
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace SMB\Command;
class Dir extends Simple {
public function __construct($connection) {
parent::__construct($connection);
$this->command = 'cd';
}
public function run($arguments) {
$arguments['postfix']=' ; dir';
return parent::run($arguments);
}
/**
* @param $lines
* @return array
*/
protected function parseOutput($lines) {
//last line is used space
array_pop($lines);
$content = array();
foreach ($lines as $line) {
$line = trim($line);
if ($line) {
list($name, $meta) = explode(" ", $line, 2);
if ($name !== '.' and $name !== '..') {
list($type, $meta) = explode(" ", trim($meta), 2);
list($size, $time) = explode(" ", trim($meta), 2);
$content[$name] = array(
'size' => intval(trim($size)),
'type' => ($type === 'D') ? 'dir' : 'file',
'time' => strtotime($time)
);
}
}
}
return $content;
}
}

30
src/command/double.php Normal file
View file

@ -0,0 +1,30 @@
<?php
/**
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace SMB\Command;
/**
* run a command with two path parameter
*/
abstract class Double extends Command {
/**
* @var string $command
*/
protected $command;
public function run($arguments) {
$path1 = $this->escapePath($arguments['path1']);
$path2 = $this->escapePath($arguments['path2']);
$share = $arguments['share'];
$postFix = (isset($arguments['postfix'])) ? $arguments['postfix'] : '';
$cmd = $this->escape('//' . $this->connection->getHost() . '/' . $share);
$cmd .= " -c '" . $this->command . ' ' . $path1 . ' ' . $path2 . $postFix . "'";
$output = $this->execute($cmd);
return $this->parseOutput($output);
}
}

46
src/command/get.php Normal file
View file

@ -0,0 +1,46 @@
<?php
/**
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace SMB\Command;
class Get extends Command {
public function __construct($connection) {
parent::__construct($connection);
$this->command = 'get';
}
public function run($arguments) {
$path1 = $this->escapePath($arguments['path1']);
$path2 = $this->escape($arguments['path2']); //seccond path is local, needs different escaping
$share = $arguments['share'];
$postFix = (isset($arguments['postfix'])) ? $arguments['postfix'] : '';
$cmd = $this->escape('//' . $this->connection->getHost() . '/' . $share);
$cmd .= " -c '" . $this->command . ' ' . $path1 . ' ' . $path2 . $postFix . "'";
$output = $this->execute($cmd);
return $this->parseOutput($output);
}
/**
* @param $lines
* @return bool
*/
protected function parseOutput($lines) {
if (count($lines) === 0) {
return true;
} else {
list($error,) = explode(' ', $lines[0]);
switch ($error) {
case 'NT_STATUS_OBJECT_PATH_NOT_FOUND':
case 'NT_STATUS_OBJECT_NAME_NOT_FOUND':
throw new \SMB\NotFoundException();
default:
throw new \Exception();
}
}
}
}

View file

@ -0,0 +1,33 @@
<?php
/**
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace SMB\Command;
class ListShares extends Command {
public function run($arguments) {
$output = $this->execute('-gL ' . $this->escape($this->connection->getHost()));
return $this->parseOutput($output);
}
/**
* @param $lines
* @return array
*/
protected function parseOutput($lines) {
$shares = array();
foreach ($lines as $line) {
if (strpos($line, '|')) {
list($type, $name, $description) = explode('|', $line);
if (strtolower($type) === 'disk') {
$shares[$name] = $description;
}
}
}
return $shares;
}
}

37
src/command/mkdir.php Normal file
View file

@ -0,0 +1,37 @@
<?php
/**
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace SMB\Command;
class Mkdir extends Simple {
public function __construct($connection) {
parent::__construct($connection);
$this->command = 'mkdir';
}
/**
* @param $lines
* @return bool
*/
protected function parseOutput($lines) {
if (count($lines) ===0) {
return true;
} else {
list($error,) = explode(' ', $lines[0]);
switch ($error) {
case 'NT_STATUS_OBJECT_PATH_NOT_FOUND':
case 'NT_STATUS_OBJECT_NAME_NOT_FOUND':
throw new \SMB\NotFoundException();
case 'NT_STATUS_OBJECT_NAME_COLLISION':
throw new \SMB\AlreadyExistsException();
default:
throw new \Exception();
}
}
}
}

49
src/command/put.php Normal file
View file

@ -0,0 +1,49 @@
<?php
/**
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace SMB\Command;
class Put extends Command {
public function __construct($connection) {
parent::__construct($connection);
$this->command = 'put';
}
public function run($arguments) {
$path1 = $this->escape($arguments['path1']); //first path is local, needs different escaping
$path2 = $this->escapePath($arguments['path2']);
$share = $arguments['share'];
$postFix = (isset($arguments['postfix'])) ? $arguments['postfix'] : '';
$cmd = $this->escape('//' . $this->connection->getHost() . '/' . $share);
$cmd .= " -c '" . $this->command . ' ' . $path1 . ' ' . $path2 . $postFix . "'";
$output = $this->execute($cmd);
return $this->parseOutput($output);
}
/**
* @param $lines
* @return bool
*/
protected function parseOutput($lines) {
if (count($lines) === 0) {
return true;
} else {
if (strpos($lines[0], 'does not exist')) {
throw new \SMB\NotFoundException();
}
list($error,) = explode(' ', $lines[0]);
switch ($error) {
case 'NT_STATUS_OBJECT_PATH_NOT_FOUND':
case 'NT_STATUS_OBJECT_NAME_NOT_FOUND':
throw new \SMB\NotFoundException();
default:
throw new \Exception();
}
}
}
}

35
src/command/rename.php Normal file
View file

@ -0,0 +1,35 @@
<?php
/**
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace SMB\Command;
class Rename extends Double {
public function __construct($connection) {
parent::__construct($connection);
$this->command = 'rename';
}
/**
* @param $lines
* @return bool
*/
protected function parseOutput($lines) {
if (count($lines) === 0) {
return true;
} else {
list($error,) = explode(' ', $lines[0]);
switch ($error) {
case 'NT_STATUS_OBJECT_PATH_NOT_FOUND':
case 'NT_STATUS_OBJECT_NAME_NOT_FOUND':
throw new \SMB\NotFoundException();
default:
throw new \Exception();
}
}
}
}

37
src/command/rmdir.php Normal file
View file

@ -0,0 +1,37 @@
<?php
/**
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace SMB\Command;
class Rmdir extends Simple {
public function __construct($connection) {
parent::__construct($connection);
$this->command = 'rmdir';
}
/**
* @param $lines
* @return bool
*/
protected function parseOutput($lines) {
if (count($lines) === 0) {
return true;
} else {
list($error,) = explode(' ', $lines[0]);
switch ($error) {
case 'NT_STATUS_OBJECT_PATH_NOT_FOUND':
case 'NT_STATUS_OBJECT_NAME_NOT_FOUND':
throw new \SMB\NotFoundException();
case 'NT_STATUS_DIRECTORY_NOT_EMPTY':
throw new \SMB\NotEmptyException();
default:
throw new \Exception();
}
}
}
}

29
src/command/simple.php Normal file
View file

@ -0,0 +1,29 @@
<?php
/**
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace SMB\Command;
/**
* run a command with one path parameter
*/
abstract class Simple extends Command {
/**
* @var string $command
*/
protected $command;
public function run($arguments) {
$path = $this->escapePath($arguments['path']);
$share = $arguments['share'];
$postFix = (isset($arguments['postfix'])) ? $arguments['postfix'] : '';
$cmd = $this->escape('//' . $this->connection->getHost() . '/' . $share);
$cmd .= " -c '" . $this->command . ' ' . $path . $postFix . "'";
$output = $this->execute($cmd);
return $this->parseOutput($output);
}
}

72
src/connection.php Normal file
View file

@ -0,0 +1,72 @@
<?php
/**
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace SMB;
class Connection {
/**
* @var string $host
*/
private $host;
/**
* @var string $user
*/
private $user;
/**
* @var string $password
*/
private $password;
/**
* @param string $host
* @param string $user
* @param string $password
*/
public function __construct($host, $user, $password) {
$this->host = $host;
$this->user = $user;
$this->password = $password;
}
/**
* @return string
*/
public function getAuthString() {
return $this->user . '%' . $this->password;
}
/**
* return string
*/
public function getHost() {
return $this->host;
}
/**
* @return Share[]
*/
public function listShares() {
$cmd = new Command\ListShares($this);
$shareNames = $cmd->run(null);
$shares = array();
foreach ($shareNames as $name => $description) {
$shares[] = new Share($this, $name);
}
return $shares;
}
/**
* @param string $name
* @return Share
*/
public function getShare($name) {
return new Share($this, $name);
}
}

18
src/errors.php Normal file
View file

@ -0,0 +1,18 @@
<?php
/**
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace SMB;
class NotFoundException extends \Exception {
}
class AlreadyExistsException extends \Exception {
}
class NotEmptyException extends \Exception {
}

103
src/share.php Normal file
View file

@ -0,0 +1,103 @@
<?php
/**
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace SMB;
class Share {
/**
* @var Connection $connection
*/
private $connection;
/**
* @var string $name
*/
private $name;
/**
* @param Connection $connection
* @param string $share
*/
public function __construct($connection, $name) {
$this->connection = $connection;
$this->name = $name;
}
/**
* List the content of a remote folder
*
* @param $path
* @return array
*/
public function dir($path) {
return (new Command\Dir($this->connection))->run(array('path' => $path, 'share' => $this->name));
}
/**
* Create a folder on the share
*
* @param string $path
* @return bool
*/
public function mkdir($path) {
return (new Command\Mkdir($this->connection))->run(array('path' => $path, 'share' => $this->name));
}
/**
* Remove a folder on the share
*
* @param string $path
* @return bool
*/
public function rmdir($path) {
return (new Command\Rmdir($this->connection))->run(array('path' => $path, 'share' => $this->name));
}
/**
* Delete a file on the share
*
* @param string $path
* @return bool
*/
public function del($path) {
return (new Command\Del($this->connection))->run(array('path' => $path, 'share' => $this->name));
}
/**
* Rename a remote file
*
* @param string $from
* @param string $to
* @return bool
*/
public function rename($from, $to) {
return (new Command\Rename($this->connection))->run(array('path1' => $from, 'path2' => $to, 'share' => $this->name));
}
/**
* Upload a local file
*
* @param string $source local file
* @param string $target remove file
* @return bool
*/
public function put($source, $target) {
return (new Command\Put($this->connection))->run(array('path1' => $source, 'path2' => $target, 'share' => $this->name));
}
/**
* Download a remote file
*
* @param string $source remove file
* @param string $target local file
* @return bool
*/
public function get($source, $target) {
return (new Command\Get($this->connection))->run(array('path1' => $source, 'path2' => $target, 'share' => $this->name));
}
}