mirror of
https://github.com/Studio-42/elFinder
synced 2026-06-08 12:37:09 +00:00
726 lines
17 KiB
PHP
726 lines
17 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @file
|
|
*
|
|
* elFinder driver for Amazon S3 (SOAP) filesystem.
|
|
*
|
|
* @author Dmitry (dio) Levashov,
|
|
* @author Alexey Sukhotin
|
|
* */
|
|
class elFinderVolumeS3 extends elFinderVolumeDriver {
|
|
protected $driverId = 's3s';
|
|
|
|
protected $s3;
|
|
|
|
public function __construct() {
|
|
$opts = array(
|
|
'accesskey' => '',
|
|
'secretkey' => '',
|
|
'bucket' => '',
|
|
'tmpPath' => '',
|
|
);
|
|
$this->options = array_merge($this->options, $opts);
|
|
$this->options['mimeDetect'] = 'internal';
|
|
|
|
}
|
|
|
|
|
|
protected function init() {
|
|
if (!$this->options['accesskey']
|
|
|| !$this->options['secretkey']
|
|
|| !$this->options['bucket']) {
|
|
return $this->setError('Required options undefined.');
|
|
}
|
|
|
|
$this->s3 = new S3SoapClient($this->options['accesskey'], $this->options['secretkey']);
|
|
|
|
$this->root = $this->options['path'];
|
|
|
|
$this->rootName = 's3';
|
|
|
|
return true;
|
|
}
|
|
|
|
protected function configure() {
|
|
parent::configure();
|
|
if (!empty($this->options['tmpPath'])) {
|
|
if ((is_dir($this->options['tmpPath']) || @mkdir($this->options['tmpPath'])) && is_writable($this->options['tmpPath'])) {
|
|
$this->tmpPath = $this->options['tmpPath'];
|
|
}
|
|
}
|
|
$this->mimeDetect = 'internal';
|
|
}
|
|
|
|
/**
|
|
* Return parent directory path
|
|
*
|
|
* @param string $path file path
|
|
* @return string
|
|
* @author Dmitry (dio) Levashov
|
|
**/
|
|
protected function _dirname($path) {
|
|
|
|
$newpath = preg_replace("/\/$/", "", $path);
|
|
$dn = substr($path, 0, strrpos($newpath, '/')) ;
|
|
|
|
if (substr($dn, 0, 1) != '/') {
|
|
$dn = "/$dn";
|
|
}
|
|
|
|
return $dn;
|
|
}
|
|
|
|
/**
|
|
* Return file name
|
|
*
|
|
* @param string $path file path
|
|
* @return string
|
|
* @author Dmitry (dio) Levashov
|
|
**/
|
|
protected function _basename($path) {
|
|
return basename($path);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Join dir name and file name and return full path.
|
|
* Some drivers (db) use int as path - so we give to concat path to driver itself
|
|
*
|
|
* @param string $dir dir path
|
|
* @param string $name file name
|
|
* @return string
|
|
* @author Dmitry (dio) Levashov
|
|
**/
|
|
protected function _joinPath($dir, $name) {
|
|
return $dir.DIRECTORY_SEPARATOR.$name;
|
|
}
|
|
|
|
/**
|
|
* Return normalized path, this works the same as os.path.normpath() in Python
|
|
*
|
|
* @param string $path path
|
|
* @return string
|
|
* @author Troex Nevelin
|
|
**/
|
|
protected function _normpath($path) {
|
|
$tmp = preg_replace("/^\//", "", $path);
|
|
$tmp = preg_replace("/\/\//", "/", $tmp);
|
|
$tmp = preg_replace("/\/$/", "", $tmp);
|
|
return $tmp;
|
|
}
|
|
|
|
/**
|
|
* Return file path related to root dir
|
|
*
|
|
* @param string $path file path
|
|
* @return string
|
|
* @author Dmitry (dio) Levashov
|
|
**/
|
|
protected function _relpath($path) {
|
|
|
|
|
|
$newpath = $path;
|
|
|
|
|
|
if (substr($path, 0, 1) != '/') {
|
|
$newpath = "/$newpath";
|
|
}
|
|
|
|
$newpath = preg_replace("/\/$/", "", $newpath);
|
|
|
|
$ret = ($newpath == $this->root) ? '' : substr($newpath, strlen($this->root)+1);
|
|
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* Convert path related to root dir into real path
|
|
*
|
|
* @param string $path file path
|
|
* @return string
|
|
* @author Dmitry (dio) Levashov
|
|
**/
|
|
protected function _abspath($path) {
|
|
return $path == $this->separator ? $this->root : $this->root.$this->separator.$path;
|
|
}
|
|
|
|
/**
|
|
* Return fake path started from root dir
|
|
*
|
|
* @param string $path file path
|
|
* @return string
|
|
* @author Dmitry (dio) Levashov
|
|
**/
|
|
protected function _path($path) {
|
|
return $this->rootName.($path == $this->root ? '' : $this->separator.$this->_relpath($path));
|
|
}
|
|
|
|
/**
|
|
* Return true if $path is children of $parent
|
|
*
|
|
* @param string $path path to check
|
|
* @param string $parent parent path
|
|
* @return bool
|
|
* @author Dmitry (dio) Levashov
|
|
**/
|
|
protected function _inpath($path, $parent) {
|
|
return $path == $parent || strpos($path, $parent.'/') === 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Converting array of objects with name and value properties to
|
|
* array[key] = value
|
|
* @param array $metadata source array
|
|
* @return array
|
|
* @author Alexey Sukhotin
|
|
**/
|
|
protected function metaobj2array($metadata) {
|
|
$arr = array();
|
|
|
|
if (is_array($metadata)) {
|
|
foreach ($metadata as $meta) {
|
|
$arr[$meta->Name] = $meta->Value;
|
|
}
|
|
} else {
|
|
$arr[$metadata->Name] = $metadata->Value;
|
|
}
|
|
return $arr;
|
|
}
|
|
|
|
/**
|
|
* Return stat for given path.
|
|
* Stat contains following fields:
|
|
* - (int) size file size in b. required
|
|
* - (int) ts file modification time in unix time. required
|
|
* - (string) mime mimetype. required for folders, others - optionally
|
|
* - (bool) read read permissions. required
|
|
* - (bool) write write permissions. required
|
|
* - (bool) locked is object locked. optionally
|
|
* - (bool) hidden is object hidden. optionally
|
|
* - (string) alias for symlinks - link target path relative to root path. optionally
|
|
* - (string) target for symlinks - link target path. optionally
|
|
*
|
|
* If file does not exists - returns empty array or false.
|
|
*
|
|
* @param string $path file path
|
|
* @return array|false
|
|
* @author Dmitry (dio) Levashov,
|
|
* @author Alexey Sukhotin
|
|
**/
|
|
protected function _stat($path) {
|
|
|
|
$stat = array(
|
|
'size' => 0,
|
|
'ts' => time(),
|
|
'read' => true,
|
|
'write' => true,
|
|
'locked' => false,
|
|
'hidden' => false,
|
|
'mime' => 'directory',
|
|
);
|
|
|
|
|
|
if ($this->root == $path) {
|
|
return $stat;
|
|
}
|
|
|
|
|
|
$np = $this->_normpath($path);
|
|
|
|
try {
|
|
$obj = $this->s3->GetObject(array('Bucket' => $this->options['bucket'], 'Key' => $np , 'GetMetadata' => true, 'InlineData' => false, 'GetData' => false));
|
|
} catch (Exception $e) {
|
|
|
|
}
|
|
|
|
if (!isset($obj) || ($obj->GetObjectResponse->Status->Code != 200)) {
|
|
$np .= '/';
|
|
try {
|
|
$obj = $this->s3->GetObject(array('Bucket' => $this->options['bucket'], 'Key' => $np , 'GetMetadata' => true, 'InlineData' => false, 'GetData' => false));
|
|
} catch (Exception $e) {
|
|
|
|
}
|
|
}
|
|
|
|
if (!(isset($obj) && $obj->GetObjectResponse->Status->Code == 200)) {
|
|
return array();
|
|
}
|
|
|
|
$mime = '';
|
|
|
|
$metadata = $this->metaobj2array($obj->GetObjectResponse->Metadata);
|
|
|
|
$mime = $metadata['Content-Type'];
|
|
|
|
if (!empty($mime)) {
|
|
$stat['mime'] = ($mime == 'binary/octet-stream') ? 'directory' : $mime;
|
|
}
|
|
|
|
if (isset($obj->GetObjectResponse->LastModified)) {
|
|
$stat['ts'] = strtotime($obj->GetObjectResponse->LastModified);
|
|
}
|
|
|
|
try {
|
|
$files = $this->s3->ListBucket(array('Bucket' => $this->options['bucket'], 'Prefix' => $np, 'Delimiter' => '/'))->ListBucketResponse->Contents;
|
|
} catch (Exception $e) {
|
|
|
|
}
|
|
|
|
if (!is_array($files)) {
|
|
$files = array($files);
|
|
}
|
|
|
|
foreach ($files as $file) {
|
|
if ($file->Key == $np) {
|
|
$stat['size'] = $file->Size;
|
|
}
|
|
}
|
|
|
|
return $stat;
|
|
}
|
|
|
|
|
|
|
|
/***************** file stat ********************/
|
|
|
|
|
|
/**
|
|
* Return true if path is dir and has at least one childs directory
|
|
*
|
|
* @param string $path dir path
|
|
* @return bool
|
|
* @author Alexey Sukhotin
|
|
**/
|
|
protected function _subdirs($path) {
|
|
$stat = $this->_stat($path);
|
|
|
|
if ($stat['mime'] == 'directory') {
|
|
$files = $this->_scandir($path);
|
|
foreach ($files as $file) {
|
|
$fstat = $this->_stat($file);
|
|
if ($fstat['mime'] == 'directory') {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Return object width and height
|
|
* Ususaly used for images, but can be realize for video etc...
|
|
*
|
|
* @param string $path file path
|
|
* @param string $mime file mime type
|
|
* @return string|false
|
|
* @author Dmitry (dio) Levashov
|
|
* @author Naoki Sawada
|
|
**/
|
|
protected function _dimensions($path, $mime) {
|
|
$ret = false;
|
|
if ($imgsize = $this->getImageSize($path, $mime)) {
|
|
$ret = $imgsize['dimensions'];
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
/******************** file/dir content *********************/
|
|
|
|
/**
|
|
* Return files list in directory
|
|
*
|
|
* @param string $path dir path
|
|
* @return array
|
|
* @author Dmitry (dio) Levashov,
|
|
* @author Alexey Sukhotin
|
|
**/
|
|
protected function _scandir($path) {
|
|
|
|
$s3path = preg_replace("/^\//", "", $path) . '/';
|
|
|
|
$files = $this->s3->ListBucket(array('Bucket' => $this->options['bucket'], 'delimiter' => '/', 'Prefix' => $s3path))->ListBucketResponse->Contents;
|
|
|
|
$finalfiles = array();
|
|
|
|
foreach ($files as $file) {
|
|
if (preg_match("|^" . $s3path . "[^/]*/?$|", $file->Key)) {
|
|
$fname = preg_replace("/\/$/", "", $file->Key);
|
|
$fname = $file->Key;
|
|
|
|
if ($fname != preg_replace("/\/$/", "", $s3path)) {
|
|
|
|
}
|
|
|
|
$finalfiles[] = $fname;
|
|
}
|
|
}
|
|
|
|
sort($finalfiles);
|
|
return $finalfiles;
|
|
}
|
|
|
|
/**
|
|
* Open file and return file pointer
|
|
*
|
|
* @param string $path file path
|
|
* @param bool $write open file for writing
|
|
* @return resource|false
|
|
* @author Dmitry (dio) Levashov,
|
|
* @author Alexey Sukhotin
|
|
**/
|
|
protected function _fopen($path, $mode="rb") {
|
|
|
|
$tn = $this->getTempFile($path);
|
|
|
|
$fp = $this->tmbPath
|
|
? @fopen($tn, 'w+')
|
|
: @tmpfile();
|
|
|
|
|
|
if ($fp) {
|
|
|
|
try {
|
|
$obj = $this->s3->GetObject(array('Bucket' => $this->options['bucket'], 'Key' => $this->_normpath($path) , 'GetMetadata' => true, 'InlineData' => true, 'GetData' => true));
|
|
} catch (Exception $e) {
|
|
|
|
}
|
|
|
|
$mime = '';
|
|
|
|
$metadata = $this->metaobj2array($obj->GetObjectResponse->Metadata);
|
|
|
|
fwrite($fp, $obj->GetObjectResponse->Data);
|
|
rewind($fp);
|
|
return $fp;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Close opened file
|
|
*
|
|
* @param resource $fp file pointer
|
|
* @param string $path file path
|
|
* @return bool
|
|
* @author Dmitry (dio) Levashov
|
|
**/
|
|
protected function _fclose($fp, $path='') {
|
|
@fclose($fp);
|
|
if ($path) {
|
|
@unlink($this->getTempFile($path));
|
|
}
|
|
}
|
|
|
|
/******************** file/dir manipulations *************************/
|
|
|
|
/**
|
|
* Create dir and return created dir path or false on failed
|
|
*
|
|
* @param string $path parent dir path
|
|
* @param string $name new directory name
|
|
* @return string|bool
|
|
* @author Dmitry (dio) Levashov,
|
|
* @author Alexey Sukhotin
|
|
**/
|
|
protected function _mkdir($path, $name) {
|
|
|
|
$newkey = $this->_normpath($path);
|
|
$newkey = preg_replace("/\/$/", "", $newkey);
|
|
$newkey = "$newkey/$name/";
|
|
|
|
try {
|
|
$obj = $this->s3->PutObjectInline(array('Bucket' => $this->options['bucket'], 'Key' => $newkey , 'ContentLength' => 0, 'Data' => ''));
|
|
} catch (Exception $e) {
|
|
|
|
}
|
|
|
|
if (isset($obj)) {
|
|
return "$path/$name";
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Create file and return it's path or false on failed
|
|
*
|
|
* @param string $path parent dir path
|
|
* @param string $name new file name
|
|
* @return string|bool
|
|
* @author Dmitry (dio) Levashov,
|
|
* @author Alexey Sukhotin
|
|
**/
|
|
protected function _mkfile($path, $name) {
|
|
$newkey = $this->_normpath($path);
|
|
$newkey = preg_replace("/\/$/", "", $newkey);
|
|
$newkey = "$newkey/$name";
|
|
|
|
try {
|
|
$obj = $this->s3->PutObjectInline(array('Bucket' => $this->options['bucket'], 'Key' => $newkey , 'ContentLength' => 0, 'Data' => '', 'Metadata' => array(array('Name' => 'Content-Type', 'Value' => 'text/plain'))));
|
|
} catch (Exception $e) {
|
|
|
|
}
|
|
|
|
if (isset($obj)) {
|
|
return "$path/$name";
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
/**
|
|
* Create symlink
|
|
*
|
|
* @param string $source file to link to
|
|
* @param string $targetDir folder to create link in
|
|
* @param string $name symlink name
|
|
* @return bool
|
|
* @author Dmitry (dio) Levashov
|
|
**/
|
|
protected function _symlink($source, $targetDir, $name) {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Copy file into another file (only inside one volume)
|
|
*
|
|
* @param string $source source file path
|
|
* @param string $target target dir path
|
|
* @param string $name file name
|
|
* @return bool
|
|
* @author Dmitry (dio) Levashov
|
|
**/
|
|
protected function _copy($source, $targetDir, $name) {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Move file into another parent dir.
|
|
* Return new file path or false.
|
|
*
|
|
* @param string $source source file path
|
|
* @param string $target target dir path
|
|
* @param string $name file name
|
|
* @return string|bool
|
|
* @author Dmitry (dio) Levashov
|
|
**/
|
|
protected function _move($source, $targetDir, $name) {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Remove file
|
|
*
|
|
* @param string $path file path
|
|
* @return bool
|
|
* @author Dmitry (dio) Levashov
|
|
**/
|
|
protected function _unlink($path) {
|
|
|
|
$newkey = $this->_normpath($path);
|
|
$newkey = preg_replace("/\/$/", "", $newkey);
|
|
|
|
try {
|
|
$obj = $this->s3->DeleteObject(array('Bucket' => $this->options['bucket'], 'Key' => $newkey));
|
|
} catch (Exception $e) {
|
|
|
|
}
|
|
|
|
/*$fp = fopen('/tmp/eltest.txt','a+');
|
|
|
|
fwrite($fp, 'key='.$newkey);*/
|
|
|
|
if (is_object($obj)) {
|
|
//fwrite($fp, 'obj='.var_export($obj,true));
|
|
|
|
if (isset($obj->DeleteObjectResponse->Code)) {
|
|
$rc = $obj->DeleteObjectResponse->Code;
|
|
|
|
if (substr($rc, 0, 1) == '2') {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//fclose($fp);
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Remove dir
|
|
*
|
|
* @param string $path dir path
|
|
* @return bool
|
|
* @author Dmitry (dio) Levashov
|
|
**/
|
|
protected function _rmdir($path) {
|
|
return $this->_unlink($path . '/');
|
|
}
|
|
|
|
/**
|
|
* Create new file and write into it from file pointer.
|
|
* Return new file path or false on error.
|
|
*
|
|
* @param resource $fp file pointer
|
|
* @param string $dir target dir path
|
|
* @param string $name file name
|
|
* @return bool|string
|
|
* @author Dmitry (dio) Levashov
|
|
**/
|
|
protected function _save($fp, $dir, $name, $mime, $stat) {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get file contents
|
|
*
|
|
* @param string $path file path
|
|
* @return string|false
|
|
* @author Dmitry (dio) Levashov
|
|
**/
|
|
protected function _getContents($path) {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Write a string to a file
|
|
*
|
|
* @param string $path file path
|
|
* @param string $content new file content
|
|
* @return bool
|
|
* @author Dmitry (dio) Levashov
|
|
**/
|
|
protected function _filePutContents($path, $content) {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Extract files from archive
|
|
*
|
|
* @param string $path file path
|
|
* @param array $arc archiver options
|
|
* @return bool
|
|
* @author Dmitry (dio) Levashov,
|
|
* @author Alexey Sukhotin
|
|
**/
|
|
protected function _extract($path, $arc) {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Create archive and return its path
|
|
*
|
|
* @param string $dir target dir
|
|
* @param array $files files names list
|
|
* @param string $name archive name
|
|
* @param array $arc archiver options
|
|
* @return string|bool
|
|
* @author Dmitry (dio) Levashov,
|
|
* @author Alexey Sukhotin
|
|
**/
|
|
protected function _archive($dir, $files, $name, $arc) {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Detect available archivers
|
|
*
|
|
* @return void
|
|
* @author Dmitry (dio) Levashov,
|
|
* @author Alexey Sukhotin
|
|
**/
|
|
protected function _checkArchivers() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* SoapClient extension with Amazon S3 WSDL and request signing support
|
|
*
|
|
* @author Alexey Sukhotin
|
|
**/
|
|
class S3SoapClient extends SoapClient {
|
|
|
|
private $accesskey = '';
|
|
private $secretkey = '';
|
|
public $client = NULL;
|
|
|
|
|
|
public function __construct($key = '', $secret = '') {
|
|
$this->accesskey = $key;
|
|
$this->secretkey = $secret;
|
|
parent::__construct('http://s3.amazonaws.com/doc/2006-03-01/AmazonS3.wsdl');
|
|
}
|
|
|
|
|
|
/**
|
|
* Method call wrapper which adding S3 signature and default arguments to all S3 operations
|
|
*
|
|
* @author Alexey Sukhotin
|
|
**/
|
|
public function __call($method, $arguments) {
|
|
|
|
/* Getting list of S3 web service functions which requires signing */
|
|
$funcs = $this->__getFunctions();
|
|
|
|
$funcnames = array();
|
|
|
|
foreach ($funcs as $func) {
|
|
preg_match("/\S+\s+([^\)]+)\(/", $func, $m);
|
|
|
|
if (isset($m[1])) {
|
|
$funcnames[] = $m[1];
|
|
}
|
|
}
|
|
|
|
/* adding signature to arguments */
|
|
if (in_array("{$method}", $funcnames)) {
|
|
|
|
if (is_array($arguments[0])) {
|
|
$arguments[0] = array_merge($arguments[0], $this->sign("{$method}"));
|
|
} else {
|
|
$arguments[0] = $this->sign("{$method}");
|
|
}
|
|
|
|
}
|
|
|
|
/*$fp = fopen('/tmp/s3debug.txt', 'a+');
|
|
fwrite($fp, 'method='."{$method}". ' timestamp='.date('Y-m-d H:i:s').' args='.var_export($arguments,true) . "\n");
|
|
fclose($fp);*/
|
|
return parent::__call($method, $arguments);
|
|
}
|
|
|
|
/**
|
|
* Generating signature and timestamp for specified S3 operation
|
|
*
|
|
* @param string $operation S3 operation name
|
|
* @return array
|
|
* @author Alexey Sukhotin
|
|
**/
|
|
protected function sign($operation) {
|
|
|
|
$params = array(
|
|
'AWSAccessKeyId' => $this->accesskey,
|
|
'Timestamp' => gmdate('Y-m-d\TH:i:s.000\Z'),
|
|
);
|
|
|
|
$sign_str = 'AmazonS3' . $operation . $params['Timestamp'];
|
|
|
|
$params['Signature'] = base64_encode(hash_hmac('sha1', $sign_str, $this->secretkey, TRUE));
|
|
|
|
return $params;
|
|
}
|
|
|
|
}
|
|
|