|
|
|
@ -22,21 +22,21 @@ |
|
|
|
|
|
|
|
|
|
namespace OC\Files\Storage; |
|
|
|
|
|
|
|
|
|
set_include_path(get_include_path() . PATH_SEPARATOR . |
|
|
|
|
\OC_App::getAppPath('files_external') . '/3rdparty/php-opencloud/lib'); |
|
|
|
|
require_once 'openstack.php'; |
|
|
|
|
|
|
|
|
|
use \OpenCloud; |
|
|
|
|
use \OpenCloud\Common\Exceptions; |
|
|
|
|
use Guzzle\Http\Exception\ClientErrorResponseException; |
|
|
|
|
use OpenCloud; |
|
|
|
|
use OpenCloud\Common\Exceptions; |
|
|
|
|
use OpenCloud\OpenStack; |
|
|
|
|
use OpenCloud\ObjectStore\Resource\DataObject; |
|
|
|
|
use OpenCloud\ObjectStore\Exception; |
|
|
|
|
|
|
|
|
|
class Swift extends \OC\Files\Storage\Common { |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @var \OpenCloud\ObjectStore |
|
|
|
|
* @var \OpenCloud\ObjectStore\Service |
|
|
|
|
*/ |
|
|
|
|
private $connection; |
|
|
|
|
/** |
|
|
|
|
* @var \OpenCloud\ObjectStore\Container |
|
|
|
|
* @var \OpenCloud\ObjectStore\Resource\Container |
|
|
|
|
*/ |
|
|
|
|
private $container; |
|
|
|
|
/** |
|
|
|
@ -62,6 +62,8 @@ class Swift extends \OC\Files\Storage\Common { |
|
|
|
|
$path = '.'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$path = str_replace('#', '%23', $path); |
|
|
|
|
|
|
|
|
|
return $path; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -82,12 +84,9 @@ class Swift extends \OC\Files\Storage\Common { |
|
|
|
|
*/ |
|
|
|
|
private function doesObjectExist($path) { |
|
|
|
|
try { |
|
|
|
|
$object = $this->container->DataObject($path); |
|
|
|
|
$this->container->getPartialObject($path); |
|
|
|
|
return true; |
|
|
|
|
} catch (Exceptions\ObjFetchError $e) { |
|
|
|
|
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
|
|
|
return false; |
|
|
|
|
} catch (Exceptions\HttpError $e) { |
|
|
|
|
} catch (ClientErrorResponseException $e) { |
|
|
|
|
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
@ -113,7 +112,6 @@ class Swift extends \OC\Files\Storage\Common { |
|
|
|
|
|
|
|
|
|
$settings = array( |
|
|
|
|
'username' => $params['user'], |
|
|
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
if (isset($params['password'])) { |
|
|
|
@ -126,19 +124,18 @@ class Swift extends \OC\Files\Storage\Common { |
|
|
|
|
$settings['tenantName'] = $params['tenant']; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$this->anchor = new \OpenCloud\OpenStack($params['url'], $settings); |
|
|
|
|
|
|
|
|
|
if (isset($params['timeout'])) { |
|
|
|
|
$this->anchor->setHttpTimeout($params['timeout']); |
|
|
|
|
$settings['timeout'] = $params['timeout']; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$this->connection = $this->anchor->ObjectStore($params['service_name'], $params['region'], 'publicURL'); |
|
|
|
|
$this->anchor = new OpenStack($params['url'], $settings); |
|
|
|
|
|
|
|
|
|
$this->connection = $this->anchor->objectStoreService($params['service_name'], $params['region']); |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
$this->container = $this->connection->Container($this->bucket); |
|
|
|
|
} catch (Exceptions\ContainerNotFoundError $e) { |
|
|
|
|
$this->container = $this->connection->Container(); |
|
|
|
|
$this->container->Create(array('name' => $this->bucket)); |
|
|
|
|
$this->container = $this->connection->getContainer($this->bucket); |
|
|
|
|
} catch (ClientErrorResponseException $e) { |
|
|
|
|
$this->container = $this->connection->createContainer($this->bucket); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!$this->file_exists('.')) { |
|
|
|
@ -158,11 +155,10 @@ class Swift extends \OC\Files\Storage\Common { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
$object = $this->container->DataObject(); |
|
|
|
|
$object->Create(array( |
|
|
|
|
'name' => $path, |
|
|
|
|
'content_type' => 'httpd/unix-directory' |
|
|
|
|
)); |
|
|
|
|
$customHeaders = array('content-type' => 'httpd/unix-directory'); |
|
|
|
|
$metadataHeaders = DataObject::stockHeaders(array()); |
|
|
|
|
$allHeaders = $customHeaders + $metadataHeaders; |
|
|
|
|
$this->container->uploadObject($path, '', $allHeaders); |
|
|
|
|
} catch (Exceptions\CreateUpdateError $e) { |
|
|
|
|
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
|
|
|
return false; |
|
|
|
@ -202,8 +198,7 @@ class Swift extends \OC\Files\Storage\Common { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
$object = $this->container->DataObject($path . '/'); |
|
|
|
|
$object->Delete(); |
|
|
|
|
$this->container->dataObject()->setName($path . '/')->delete(); |
|
|
|
|
} catch (Exceptions\DeleteError $e) { |
|
|
|
|
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
|
|
|
return false; |
|
|
|
@ -221,15 +216,19 @@ class Swift extends \OC\Files\Storage\Common { |
|
|
|
|
$path .= '/'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$path = str_replace('%23', '#', $path); // the prefix is sent as a query param, so revert the encoding of # |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
$files = array(); |
|
|
|
|
$objects = $this->container->ObjectList(array( |
|
|
|
|
/** @var OpenCloud\Common\Collection $objects */ |
|
|
|
|
$objects = $this->container->objectList(array( |
|
|
|
|
'prefix' => $path, |
|
|
|
|
'delimiter' => '/' |
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
while ($object = $objects->Next()) { |
|
|
|
|
$file = basename($object->Name()); |
|
|
|
|
/** @var OpenCloud\ObjectStore\Resource\DataObject $object */ |
|
|
|
|
foreach ($objects as $object) { |
|
|
|
|
$file = basename($object->getName()); |
|
|
|
|
if ($file !== basename($path)) { |
|
|
|
|
$files[] = $file; |
|
|
|
|
} |
|
|
|
@ -252,15 +251,22 @@ class Swift extends \OC\Files\Storage\Common { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
$object = $this->container->DataObject($path); |
|
|
|
|
} catch (Exceptions\ObjFetchError $e) { |
|
|
|
|
$object = $this->container->getPartialObject($path); |
|
|
|
|
} catch (ClientErrorResponseException $e) { |
|
|
|
|
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$mtime = $object->extra_headers['X-Timestamp']; |
|
|
|
|
if (isset($object->extra_headers['X-Object-Meta-Timestamp'])) { |
|
|
|
|
$mtime = $object->extra_headers['X-Object-Meta-Timestamp']; |
|
|
|
|
$dateTime = \DateTime::createFromFormat(\DateTime::RFC1123, $object->getLastModified()); |
|
|
|
|
if ($dateTime !== false) { |
|
|
|
|
$mtime = $dateTime->getTimestamp(); |
|
|
|
|
} else { |
|
|
|
|
$mtime = null; |
|
|
|
|
} |
|
|
|
|
$objectMetadata = $object->getMetadata(); |
|
|
|
|
$metaTimestamp = $objectMetadata->getProperty('timestamp'); |
|
|
|
|
if (isset($metaTimestamp)) { |
|
|
|
|
$mtime = $metaTimestamp; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!empty($mtime)) { |
|
|
|
@ -268,7 +274,7 @@ class Swift extends \OC\Files\Storage\Common { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$stat = array(); |
|
|
|
|
$stat['size'] = $object->content_length; |
|
|
|
|
$stat['size'] = (int) $object->getContentLength(); |
|
|
|
|
$stat['mtime'] = $mtime; |
|
|
|
|
$stat['atime'] = time(); |
|
|
|
|
return $stat; |
|
|
|
@ -293,13 +299,13 @@ class Swift extends \OC\Files\Storage\Common { |
|
|
|
|
public function unlink($path) { |
|
|
|
|
$path = $this->normalizePath($path); |
|
|
|
|
|
|
|
|
|
if ($this->is_dir($path)) { |
|
|
|
|
return $this->rmdir($path); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
$object = $this->container->DataObject($path); |
|
|
|
|
$object->Delete(); |
|
|
|
|
} catch (Exceptions\DeleteError $e) { |
|
|
|
|
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
|
|
|
return false; |
|
|
|
|
} catch (Exceptions\ObjFetchError $e) { |
|
|
|
|
$this->container->dataObject()->setName($path)->delete(); |
|
|
|
|
} catch (ClientErrorResponseException $e) { |
|
|
|
|
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
@ -316,13 +322,19 @@ class Swift extends \OC\Files\Storage\Common { |
|
|
|
|
$tmpFile = \OC_Helper::tmpFile(); |
|
|
|
|
self::$tmpFiles[$tmpFile] = $path; |
|
|
|
|
try { |
|
|
|
|
$object = $this->container->DataObject($path); |
|
|
|
|
} catch (Exceptions\ObjFetchError $e) { |
|
|
|
|
$object = $this->container->getObject($path); |
|
|
|
|
} catch (ClientErrorResponseException $e) { |
|
|
|
|
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
|
|
|
return false; |
|
|
|
|
} catch (Exception\ObjectNotFoundException $e) { |
|
|
|
|
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
try { |
|
|
|
|
$object->SaveToFilename($tmpFile); |
|
|
|
|
$objectContent = $object->getContent(); |
|
|
|
|
$objectContent->rewind(); |
|
|
|
|
$stream = $objectContent->getStream(); |
|
|
|
|
file_put_contents($tmpFile, $stream); |
|
|
|
|
} catch (Exceptions\IOError $e) { |
|
|
|
|
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
|
|
|
return false; |
|
|
|
@ -363,43 +375,32 @@ class Swift extends \OC\Files\Storage\Common { |
|
|
|
|
if ($this->is_dir($path)) { |
|
|
|
|
return 'httpd/unix-directory'; |
|
|
|
|
} else if ($this->file_exists($path)) { |
|
|
|
|
$object = $this->container->DataObject($path); |
|
|
|
|
return $object->extra_headers["Content-Type"]; |
|
|
|
|
$object = $this->container->getPartialObject($path); |
|
|
|
|
return $object->getContentType(); |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function touch($path, $mtime = null) { |
|
|
|
|
$path = $this->normalizePath($path); |
|
|
|
|
if (is_null($mtime)) { |
|
|
|
|
$mtime = time(); |
|
|
|
|
} |
|
|
|
|
$metadata = array('timestamp' => $mtime); |
|
|
|
|
if ($this->file_exists($path)) { |
|
|
|
|
if ($this->is_dir($path) && $path != '.') { |
|
|
|
|
$path .= '/'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$object = $this->container->DataObject($path); |
|
|
|
|
if( is_null($mtime)) { |
|
|
|
|
$mtime = time(); |
|
|
|
|
} |
|
|
|
|
$settings = array( |
|
|
|
|
'name' => $path, |
|
|
|
|
'extra_headers' => array( |
|
|
|
|
'X-Object-Meta-Timestamp' => $mtime |
|
|
|
|
) |
|
|
|
|
); |
|
|
|
|
return $object->UpdateMetadata($settings); |
|
|
|
|
$object = $this->container->getPartialObject($path); |
|
|
|
|
$object->saveMetadata($metadata); |
|
|
|
|
return true; |
|
|
|
|
} else { |
|
|
|
|
$object = $this->container->DataObject(); |
|
|
|
|
if (is_null($mtime)) { |
|
|
|
|
$mtime = time(); |
|
|
|
|
} |
|
|
|
|
$settings = array( |
|
|
|
|
'name' => $path, |
|
|
|
|
'content_type' => 'text/plain', |
|
|
|
|
'extra_headers' => array( |
|
|
|
|
'X-Object-Meta-Timestamp' => $mtime |
|
|
|
|
) |
|
|
|
|
); |
|
|
|
|
return $object->Create($settings); |
|
|
|
|
$customHeaders = array('content-type' => 'text/plain'); |
|
|
|
|
$metadataHeaders = DataObject::stockHeaders($metadata); |
|
|
|
|
$allHeaders = $customHeaders + $metadataHeaders; |
|
|
|
|
$this->container->uploadObject($path, '', $allHeaders); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -407,31 +408,29 @@ class Swift extends \OC\Files\Storage\Common { |
|
|
|
|
$path1 = $this->normalizePath($path1); |
|
|
|
|
$path2 = $this->normalizePath($path2); |
|
|
|
|
|
|
|
|
|
if ($this->is_file($path1)) { |
|
|
|
|
$fileType = $this->filetype($path1); |
|
|
|
|
if ($fileType === 'file') { |
|
|
|
|
|
|
|
|
|
// make way |
|
|
|
|
$this->unlink($path2); |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
$source = $this->container->DataObject($path1); |
|
|
|
|
$target = $this->container->DataObject(); |
|
|
|
|
$target->Create(array( |
|
|
|
|
'name' => $path2, |
|
|
|
|
)); |
|
|
|
|
$source->Copy($target); |
|
|
|
|
} catch (Exceptions\ObjectCopyError $e) { |
|
|
|
|
$source = $this->container->getPartialObject($path1); |
|
|
|
|
$source->copy($this->bucket.'/'.$path2); |
|
|
|
|
} catch (ClientErrorResponseException $e) { |
|
|
|
|
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
if ($this->file_exists($path2)) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} else if ($fileType === 'dir') { |
|
|
|
|
|
|
|
|
|
// make way |
|
|
|
|
$this->unlink($path2); |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
$source = $this->container->DataObject($path1 . '/'); |
|
|
|
|
$target = $this->container->DataObject(); |
|
|
|
|
$target->Create(array( |
|
|
|
|
'name' => $path2 . '/', |
|
|
|
|
)); |
|
|
|
|
$source->Copy($target); |
|
|
|
|
} catch (Exceptions\ObjectCopyError $e) { |
|
|
|
|
$source = $this->container->getPartialObject($path1 . '/'); |
|
|
|
|
$source->copy($this->bucket.'/'.$path2 . '/'); |
|
|
|
|
} catch (ClientErrorResponseException $e) { |
|
|
|
|
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
@ -446,6 +445,10 @@ class Swift extends \OC\Files\Storage\Common { |
|
|
|
|
$target = $path2 . '/' . $file; |
|
|
|
|
$this->copy($source, $target); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
//file does not exist |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
@ -455,31 +458,28 @@ class Swift extends \OC\Files\Storage\Common { |
|
|
|
|
$path1 = $this->normalizePath($path1); |
|
|
|
|
$path2 = $this->normalizePath($path2); |
|
|
|
|
|
|
|
|
|
if ($this->is_file($path1)) { |
|
|
|
|
$fileType = $this->filetype($path1); |
|
|
|
|
|
|
|
|
|
if ($fileType === 'dir' || $fileType === 'file') { |
|
|
|
|
|
|
|
|
|
// make way |
|
|
|
|
$this->unlink($path2); |
|
|
|
|
|
|
|
|
|
// copy |
|
|
|
|
if ($this->copy($path1, $path2) === false) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// cleanup |
|
|
|
|
if ($this->unlink($path1) === false) { |
|
|
|
|
$this->unlink($path2); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
if ($this->file_exists($path2)) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ($this->copy($path1, $path2) === false) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if ($this->rmdir($path1) === false) { |
|
|
|
|
$this->rmdir($path2); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function getId() { |
|
|
|
@ -494,12 +494,8 @@ class Swift extends \OC\Files\Storage\Common { |
|
|
|
|
if (!isset(self::$tmpFiles[$tmpFile])) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$object = $this->container->DataObject(); |
|
|
|
|
$object->Create(array( |
|
|
|
|
'name' => self::$tmpFiles[$tmpFile], |
|
|
|
|
'content_type' => \OC_Helper::getMimeType($tmpFile) |
|
|
|
|
), $tmpFile); |
|
|
|
|
$fileData = fopen($tmpFile, 'r'); |
|
|
|
|
$this->container->uploadObject(self::$tmpFiles[$tmpFile], $fileData); |
|
|
|
|
unlink($tmpFile); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|