Introduce FileInfo objects for the result of dir and add readable and hidden attributes to files

This commit is contained in:
Robin Appelman 2014-07-31 16:05:20 +02:00
commit 1c11289d36
7 changed files with 452 additions and 53 deletions

111
src/FileInfo.php Normal file
View file

@ -0,0 +1,111 @@
<?php
/**
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Licensed under the MIT license:
* http://opensource.org/licenses/MIT
*/
namespace Icewind\SMB;
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;
/**
* @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);
}
}

45
src/IFileInfo.php Normal file
View file

@ -0,0 +1,45 @@
<?php
/**
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Licensed under the MIT license:
* http://opensource.org/licenses/MIT
*/
namespace Icewind\SMB;
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();
}

View file

@ -98,7 +98,7 @@ interface IShare {
* ]
*
* @param $path
* @return array[]
* @return \Icewind\SMB\IFileInfo[]
*
* @throws \Icewind\SMB\NotFoundException
* @throws \Icewind\SMB\InvalidTypeException

122
src/NativeFileInfo.php Normal file
View file

@ -0,0 +1,122 @@
<?php
/**
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Licensed under the MIT license:
* http://opensource.org/licenses/MIT
*/
namespace Icewind\SMB;
class NativeFileInfo implements IFileInfo {
const MODE_FILE = 0100000;
/**
* @var string
*/
protected $path;
/**
* @var string
*/
protected $name;
/**
* @var \Icewind\SMB\NativeShare
*/
protected $share;
/**
* @var array
*/
protected $statCache;
/**
* @var int
*/
protected $modeCache;
/**
* @param \Icewind\SMB\NativeShare $share
* @param string $path
* @param string $name
*/
public function __construct($share, $path, $name) {
$this->share = $share;
$this->path = $path;
$this->name = $name;
}
/**
* @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->stat($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) {
$this->modeCache = $this->share->getAttribute($this->path, 'system.dos_attr.mode');
}
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);
}
}

View file

@ -101,7 +101,8 @@ class NativeShare implements IShare {
} else if (strpos($errorString, 'unknown error (110)') or
strpos($errorString, 'unknown error (111)') or
strpos($errorString, 'unknown error (112)') or
strpos($errorString, 'unknown error (113)')) {
strpos($errorString, 'unknown error (113)')
) {
// errors for connection timeout, connection refused, host is down and
// no route to host, respectively
throw new ConnectionError($errorString);
@ -138,12 +139,7 @@ class NativeShare implements IShare {
while ($file = smbclient_readdir($this->state, $dh)) {
$name = $file['name'];
if ($name !== '.' and $name !== '..') {
$stat = $this->stat($path . '/' . $name);
$files[$name] = array(
'type' => ($file['type'] === 'directory') ? 'dir' : 'file',
'size' => $stat['size'],
'time' => $stat['mtime']
);
$files [] = new NativeFileInfo($this, $path . '/' . $name, $name);
}
}
smbclient_closedir($this->state, $dh);
@ -151,7 +147,7 @@ class NativeShare implements IShare {
return $files;
}
protected function stat($path) {
public function stat($path) {
$this->connect();
self::registerErrorHandler();
$stat = smbclient_stat($this->state, $this->buildUrl($path));
@ -325,4 +321,37 @@ class NativeShare implements IShare {
$handle = $this->create($source);
return NativeStream::wrap($this->state, $handle, 'w');
}
/**
* List the available extended attributes for the path (returns a fixed list)
*
* @param string $path
* @return array list the available attributes for the path
*/
public function listAttributes($path) {
$this->connect();
self::registerErrorHandler();
$result = smbclient_listxattr($this->state, $this->buildUrl($path));
self::restoreErrorHandler();
return $result;
}
/**
* 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();
self::registerErrorHandler();
$result = smbclient_getxattr($this->state, $this->buildUrl($path), $attribute);
self::restoreErrorHandler();
// parse hex string
if ($attribute === 'system.dos_attr.mode') {
$result = hexdec(substr($result, 2));
}
return $result;
}
}

View file

@ -80,25 +80,15 @@ class Share implements IShare {
/**
* List the content of a remote folder
*
* Returns a nested array in the format of
* [
* $name => [
* 'size' => $size,
* 'type' => $type,
* 'time' => $mtime
* ],
* ...
* ]
*
* @param $path
* @return array[]
* @return \Icewind\SMB\IFileInfo[]
*
* @throws \Icewind\SMB\NotFoundException
* @throws \Icewind\SMB\InvalidTypeException
*/
public function dir($path) {
$path = $this->escapePath($path);
$output = $this->execute('cd ' . $path);
$escapedPath = $this->escapePath($path);
$output = $this->execute('cd ' . $escapedPath);
//check output for errors
$this->parseOutput($output);
$output = $this->execute('dir');
@ -111,13 +101,11 @@ class Share implements IShare {
$content = array();
foreach ($output as $line) {
if (preg_match($regex, $line, $matches)) {
list(, $name, $type, $size, $time) = $matches;
list(, $name, $mode, $size, $time) = $matches;
if ($name !== '.' and $name !== '..') {
$content[$name] = array(
'size' => intval(trim($size)),
'type' => (strpos($type, 'D') !== false) ? 'dir' : 'file',
'time' => strtotime($time . ' ' . $this->getServerTimeZone())
);
$mode = $this->parseMode($mode);
$time = strtotime($time . ' ' . $this->getServerTimeZone());
$content[] = new FileInfo($path . '/' . $name, $name, $size, $time, $mode);
}
}
}
@ -250,7 +238,6 @@ class Share implements IShare {
$connection = new Connection($command);
$connection->writeAuthentication($this->server->getUser(), $this->server->getPassword());
$fh = $connection->getFileOutputStream();
//save the connection as context of the stream to prevent it going out of scope and cleaning up the resource
stream_context_set_option($fh, 'file', 'connection', $connection);
return $fh;
}
@ -284,6 +271,82 @@ class Share implements IShare {
});
}
/**
* @param string $path
* @return array
*/
protected function getAttributes($path) {
$path = $this->escapePath($path);
$output = $this->execute('allinfo ' . $path);
$attributes = array();
foreach ($output as $line) {
list($name, $value) = explode($line, ':', 2);
$value = trim($value);
switch ($name) {
case 'create_time':
$attributes['system.dos_attr.c_time'] = strtotime($value . ' ' . $this->getServerTimeZone());
break;
case 'access_time':
$attributes['system.dos_attr.a_time'] = strtotime($value . ' ' . $this->getServerTimeZone());
break;
case 'change_time':
$attributes['system.dos_attr.m_time'] = strtotime($value . ' ' . $this->getServerTimeZone());
break;
case 'attributes':
$attributes['system.dos_attr.mode'] = $this->parseMode($value);
break;
}
}
return $attributes;
}
/**
* @param string $mode
* @return string
*/
protected 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
);
foreach ($modeStrings as $char => $val) {
if (strpos($mode, $char) !== false) {
$result |= $val;
}
}
return $result;
}
/**
* List the available extended attributes for the path (returns a fixed list)
*
* @param string $path
* @return array list the available attributes for the path
*/
public function listAttributes($path) {
return array_keys($this->getAttributes($path));
}
/**
* 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) {
$attributes = $this->getAttributes($path);
if (isset($attributes[$attribute])) {
return $attributes[$attribute];
} else {
return null;
}
}
/**
* @param string $command
* @return array