diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/base.php | 14 | ||||
-rw-r--r-- | lib/private/defaults.php | 7 | ||||
-rw-r--r-- | lib/private/files/filesystem.php | 31 | ||||
-rw-r--r-- | lib/private/files/mount/mount.php | 7 | ||||
-rw-r--r-- | lib/private/files/objectstore/homeobjectstorestorage.php | 71 | ||||
-rw-r--r-- | lib/private/files/objectstore/noopscanner.php | 74 | ||||
-rw-r--r-- | lib/private/files/objectstore/objectstorestorage.php | 421 | ||||
-rw-r--r-- | lib/private/files/objectstore/swift.php | 147 | ||||
-rw-r--r-- | lib/private/files/storage/common.php | 5 | ||||
-rw-r--r-- | lib/private/files/storage/dav.php | 69 | ||||
-rwxr-xr-x | lib/private/util.php | 193 | ||||
-rw-r--r-- | lib/public/files/objectstore/iobjectstore.php | 33 |
12 files changed, 975 insertions, 97 deletions
diff --git a/lib/base.php b/lib/base.php index eef094737b6..d9fe94f1ccc 100644 --- a/lib/base.php +++ b/lib/base.php @@ -179,20 +179,22 @@ class OC { } public static function checkConfig() { + $l = OC_L10N::get('lib'); if (file_exists(self::$configDir . "/config.php") and !is_writable(self::$configDir . "/config.php") ) { if (self::$CLI) { - echo "Can't write into config directory!\n"; - echo "This can usually be fixed by giving the webserver write access to the config directory\n"; + echo $l->t('Cannot write into "config" directory!')."\n"; + echo $l->t('This can usually be fixed by giving the webserver write access to the config directory')."\n"; echo "\n"; - echo "See " . \OC_Helper::linkToDocs('admin-dir_permissions') . "\n"; + echo $l->t('See %s', array(\OC_Helper::linkToDocs('admin-dir_permissions')))."\n"; exit; } else { OC_Template::printErrorPage( - "Can't write into config directory!", - 'This can usually be fixed by ' - . '<a href="' . \OC_Helper::linkToDocs('admin-dir_permissions') . '" target="_blank">giving the webserver write access to the config directory</a>.' + $l->t('Cannot write into "config" directory!'), + $l->t('This can usually be fixed by ' + . '%sgiving the webserver write access to the config directory%s.', + array('<a href="'.\OC_Helper::linkToDocs('admin-dir_permissions').'" target="_blank">', '</a>')) ); } } diff --git a/lib/private/defaults.php b/lib/private/defaults.php index 663c327a3b0..c039c30fbb9 100644 --- a/lib/private/defaults.php +++ b/lib/private/defaults.php @@ -19,12 +19,14 @@ class OC_Defaults { private $defaultBaseUrl; private $defaultSyncClientUrl; private $defaultDocBaseUrl; + private $defaultDocVersion; private $defaultSlogan; private $defaultLogoClaim; private $defaultMailHeaderColor; function __construct() { - $this->l = OC_L10N::get('core'); + $this->l = OC_L10N::get('lib'); + $version = OC_Util::getVersion(); $this->defaultEntity = "ownCloud"; /* e.g. company name, used for footers and copyright notices */ $this->defaultName = "ownCloud"; /* short name, used when referring to the software */ @@ -32,6 +34,7 @@ class OC_Defaults { $this->defaultBaseUrl = "https://owncloud.org"; $this->defaultSyncClientUrl = "https://owncloud.org/sync-clients/"; $this->defaultDocBaseUrl = "http://doc.owncloud.org"; + $this->defaultDocVersion = $version[0] . ".0"; // used to generate doc links $this->defaultSlogan = $this->l->t("web services under your control"); $this->defaultLogoClaim = ""; $this->defaultMailHeaderColor = "#1d2d44"; /* header color of mail notifications */ @@ -180,7 +183,7 @@ class OC_Defaults { if ($this->themeExist('buildDocLinkToKey')) { return $this->theme->buildDocLinkToKey($key); } - return $this->getDocBaseUrl() . '/server/6.0/go.php?to=' . $key; + return $this->getDocBaseUrl() . '/server/' . $this->defaultDocVersion . '/go.php?to=' . $key; } /** diff --git a/lib/private/files/filesystem.php b/lib/private/files/filesystem.php index 2cc4a2130eb..4774d25ad9e 100644 --- a/lib/private/files/filesystem.php +++ b/lib/private/files/filesystem.php @@ -325,13 +325,36 @@ class Filesystem { $userObject = \OC_User::getManager()->get($user); if (!is_null($userObject)) { + $homeStorage = \OC_Config::getValue( 'objectstore' ); + if (!empty($homeStorage)) { + // sanity checks + if (empty($homeStorage['class'])) { + \OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR); + } + if (!isset($homeStorage['arguments'])) { + $homeStorage['arguments'] = array(); + } + // instantiate object store implementation + $homeStorage['arguments']['objectstore'] = new $homeStorage['class']($homeStorage['arguments']); + // mount with home object store implementation + $homeStorage['class'] = '\OC\Files\ObjectStore\HomeObjectStoreStorage'; + } else { + $homeStorage = array( + //default home storage configuration: + 'class' => '\OC\Files\Storage\Home', + 'arguments' => array() + ); + } + $homeStorage['arguments']['user'] = $userObject; + // check for legacy home id (<= 5.0.12) if (\OC\Files\Cache\Storage::exists('local::' . $root . '/')) { - self::mount('\OC\Files\Storage\Home', array('user' => $userObject, 'legacy' => true), $user); - } - else { - self::mount('\OC\Files\Storage\Home', array('user' => $userObject), $user); + $homeStorage['arguments']['legacy'] = true; } + + self::mount($homeStorage['class'], $homeStorage['arguments'], $user); + + $home = \OC\Files\Filesystem::getStorage($user); } else { self::mount('\OC\Files\Storage\Local', array('datadir' => $root), $user); diff --git a/lib/private/files/mount/mount.php b/lib/private/files/mount/mount.php index 04bccbcab87..48c9d88c23c 100644 --- a/lib/private/files/mount/mount.php +++ b/lib/private/files/mount/mount.php @@ -93,7 +93,12 @@ class Mount { try { return $this->loader->load($this->mountPoint, $this->class, $this->arguments); } catch (\Exception $exception) { - \OC_Log::write('core', $exception->getMessage(), \OC_Log::ERROR); + if ($this->mountPoint === '/') { + // the root storage could not be initialized, show the user! + throw new \Exception('The root storage could not be initialized. Please contact your local administrator.', $exception->getCode(), $exception); + } else { + \OC_Log::write('core', $exception->getMessage(), \OC_Log::ERROR); + } return null; } } else { diff --git a/lib/private/files/objectstore/homeobjectstorestorage.php b/lib/private/files/objectstore/homeobjectstorestorage.php new file mode 100644 index 00000000000..26a2788d860 --- /dev/null +++ b/lib/private/files/objectstore/homeobjectstorestorage.php @@ -0,0 +1,71 @@ +<?php +/** + * @author Jörn Friedrich Dreyer + * @copyright (c) 2014 Jörn Friedrich Dreyer <jfd@owncloud.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OC\Files\ObjectStore; + +use OC\User\User; + +class HomeObjectStoreStorage extends ObjectStoreStorage { + + /** + * The home user storage requires a user object to create a unique storage id + * @param array $params + */ + public function __construct($params) { + if ( ! isset($params['user']) || ! $params['user'] instanceof User) { + throw new \Exception('missing user object in parameters'); + } + $this->user = $params['user']; + parent::__construct($params); + + + //initialize cache with root directory in cache + if ( ! $this->is_dir('files') ) { + $this->mkdir('files'); + } + } + + public function getId () { + return 'object::user:' . $this->user->getUID(); + } + + /** + * get the owner of a path + * + * @param string $path The path to get the owner + * @return false|string uid + */ + public function getOwner($path) { + if (is_object($this->user)) { + return $this->user->getUID(); + } + return false; + } + + /** + * @param string $path, optional + * @return \OC\User\User + */ + public function getUser($path = null) { + return $this->user; + } + + +}
\ No newline at end of file diff --git a/lib/private/files/objectstore/noopscanner.php b/lib/private/files/objectstore/noopscanner.php new file mode 100644 index 00000000000..59ca1771971 --- /dev/null +++ b/lib/private/files/objectstore/noopscanner.php @@ -0,0 +1,74 @@ +<?php +/** + * @author Jörn Friedrich Dreyer + * @copyright (c) 2014 Jörn Friedrich Dreyer <jfd@owncloud.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OC\Files\ObjectStore; +use \OC\Files\Cache\Scanner; +use \OC\Files\Storage\Storage; + +class NoopScanner extends Scanner { + + public function __construct(Storage $storage) { + //we don't need the storage, so do nothing here + } + + /** + * scan a single file and store it in the cache + * + * @param string $file + * @param int $reuseExisting + * @param bool $parentExistsInCache + * @return array with metadata of the scanned file + */ + public function scanFile($file, $reuseExisting = 0, $parentExistsInCache = false) { + return array(); + } + + /** + * scan a folder and all it's children + * + * @param string $path + * @param bool $recursive + * @param int $reuse + * @return array with the meta data of the scanned file or folder + */ + public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1) { + return array(); + } + + /** + * scan all the files and folders in a folder + * + * @param string $path + * @param bool $recursive + * @param int $reuse + * @return int the size of the scanned folder or -1 if the size is unknown at this stage + */ + public function scanChildren($path, $recursive = Storage::SCAN_RECURSIVE, $reuse = -1) { + $size = 0; + return $size; + } + + /** + * walk over any folders that are not fully scanned yet and scan them + */ + public function backgroundScan() { + //noop + } +} diff --git a/lib/private/files/objectstore/objectstorestorage.php b/lib/private/files/objectstore/objectstorestorage.php new file mode 100644 index 00000000000..85f43b90cbb --- /dev/null +++ b/lib/private/files/objectstore/objectstorestorage.php @@ -0,0 +1,421 @@ +<?php +/** + * @author Jörn Friedrich Dreyer + * @copyright (c) 2014 Jörn Friedrich Dreyer <jfd@owncloud.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OC\Files\ObjectStore; + +use OC\Files\Filesystem; +use OCP\Files\ObjectStore\IObjectStore; + +class ObjectStoreStorage extends \OC\Files\Storage\Common { + + /** + * @var array + */ + private static $tmpFiles = array(); + /** + * @var \OCP\Files\ObjectStore\IObjectStore $objectStore + */ + protected $objectStore; + /** + * @var \OC\User\User $user + */ + protected $user; + + public function __construct($params) { + if (isset($params['objectstore']) && $params['objectstore'] instanceof IObjectStore) { + $this->objectStore = $params['objectstore']; + } else { + throw new \Exception('missing IObjectStore instance'); + } + if (isset($params['storageid'])) { + $this->id = 'object::store:' . $params['storageid']; + } else { + $this->id = 'object::store:' . $this->objectStore->getStorageId(); + } + //initialize cache with root directory in cache + if (!$this->is_dir('/')) { + $this->mkdir('/'); + } + } + + public function mkdir($path) { + $path = $this->normalizePath($path); + + if ($this->is_dir($path)) { + return false; + } + + $dirName = $this->normalizePath(dirname($path)); + $parentExists = $this->is_dir($dirName); + + $mTime = time(); + + $data = array( + 'mimetype' => 'httpd/unix-directory', + 'size' => 0, + 'mtime' => $mTime, + 'storage_mtime' => $mTime, + 'permissions' => \OCP\PERMISSION_ALL, + ); + + if ($dirName === '' && !$parentExists) { + //create root on the fly + $data['etag'] = $this->getETag(''); + $this->getCache()->put('', $data); + $parentExists = true; + + // we are done when the root folder was meant to be created + if ($dirName === $path) { + return true; + } + } + + if ($parentExists) { + $data['etag'] = $this->getETag($path); + $this->getCache()->put($path, $data); + return true; + } + return false; + } + + /** + * @param string $path + * @return string + */ + private function normalizePath($path) { + $path = trim($path, '/'); + //FIXME why do we sometimes get a path like 'files//username'? + $path = str_replace('//', '/', $path); + + // dirname('/folder') returns '.' but internally (in the cache) we store the root as '' + if (!$path || $path === '.') { + $path = ''; + } + + return $path; + } + + /** + * Object Stores use a NoopScanner because metadata is directly stored in + * the file cache and cannot really scan the filesystem. The storage passed in is not used anywhere. + * + * @param string $path + * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner + * @return \OC\Files\ObjectStore\NoopScanner + */ + public function getScanner($path = '', $storage = null) { + if (!$storage) { + $storage = $this; + } + if (!isset($this->scanner)) { + $this->scanner = new NoopScanner($storage); + } + return $this->scanner; + } + + public function getId() { + return $this->id; + } + + public function rmdir($path) { + $path = $this->normalizePath($path); + + if (!$this->is_dir($path)) { + return false; + } + + $this->rmObjects($path); + + $this->getCache()->remove($path); + + return true; + } + + private function rmObjects($path) { + $children = $this->getCache()->getFolderContents($path); + foreach ($children as $child) { + if ($child['mimetype'] === 'httpd/unix-directory') { + $this->rmObjects($child['path']); + } else { + $this->unlink($child['path']); + } + } + } + + public function unlink($path) { + $path = $this->normalizePath($path); + $stat = $this->stat($path); + + if ($stat && isset($stat['fileid'])) { + if ($stat['mimetype'] === 'httpd/unix-directory') { + return $this->rmdir($path); + } + try { + $this->objectStore->deleteObject($this->getURN($stat['fileid'])); + } catch (\Exception $ex) { + if ($ex->getCode() !== 404) { + \OCP\Util::writeLog('objectstore', 'Could not delete object: ' . $ex->getMessage(), \OCP\Util::ERROR); + return false; + } else { + //removing from cache is ok as it does not exist in the objectstore anyway + } + } + $this->getCache()->remove($path); + return true; + } + return false; + } + + public function stat($path) { + $path = $this->normalizePath($path); + return $this->getCache()->get($path); + } + + /** + * Override this method if you need a different unique resource identifier for your object storage implementation. + * The default implementations just appends the fileId to 'urn:oid:'. Make sure the URN is unique over all users. + * You may need a mapping table to store your URN if it cannot be generated from the fileid. + * + * @param int $fileId the fileid + * @return null|string the unified resource name used to identify the object + */ + protected function getURN($fileId) { + if (is_numeric($fileId)) { + return 'urn:oid:' . $fileId; + } + return null; + } + + public function opendir($path) { + $path = $this->normalizePath($path); + + try { + $files = array(); + $folderContents = $this->getCache()->getFolderContents($path); + foreach ($folderContents as $file) { + $files[] = $file['name']; + } + + \OC\Files\Stream\Dir::register('objectstore' . $path . '/', $files); + + return opendir('fakedir://objectstore' . $path . '/'); + } catch (Exception $e) { + \OCP\Util::writeLog('objectstore', $e->getMessage(), \OCP\Util::ERROR); + return false; + } + } + + public function filetype($path) { + $path = $this->normalizePath($path); + $stat = $this->stat($path); + if ($stat) { + if ($stat['mimetype'] === 'httpd/unix-directory') { + return 'dir'; + } + return 'file'; + } else { + return false; + } + } + + public function fopen($path, $mode) { + $path = $this->normalizePath($path); + + switch ($mode) { + case 'r': + case 'rb': + $stat = $this->stat($path); + if (is_array($stat)) { + try { + return $this->objectStore->readObject($this->getURN($stat['fileid'])); + } catch (\Exception $ex) { + \OCP\Util::writeLog('objectstore', 'Could not get object: ' . $ex->getMessage(), \OCP\Util::ERROR); + return false; + } + } else { + return false; + } + case 'w': + case 'wb': + case 'a': + case 'ab': + case 'r+': + case 'w+': + case 'wb+': + case 'a+': + case 'x': + case 'x+': + case 'c': + case 'c+': + if (strrpos($path, '.') !== false) { + $ext = substr($path, strrpos($path, '.')); + } else { + $ext = ''; + } + $tmpFile = \OC_Helper::tmpFile($ext); + \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); + if ($this->file_exists($path)) { + $source = $this->fopen($path, 'r'); + file_put_contents($tmpFile, $source); + } + self::$tmpFiles[$tmpFile] = $path; + + return fopen('close://' . $tmpFile, $mode); + } + return false; + } + + public function file_exists($path) { + $path = $this->normalizePath($path); + return (bool)$this->stat($path); + } + + public function rename($source, $target) { + $source = $this->normalizePath($source); + $target = $this->normalizePath($target); + $stat1 = $this->stat($source); + if (isset($stat1['mimetype']) && $stat1['mimetype'] === 'httpd/unix-directory') { + $this->remove($target); + $dir = $this->opendir($source); + $this->mkdir($target); + while ($file = readdir($dir)) { + if (!Filesystem::isIgnoredDir($file)) { + if (!$this->rename($source . '/' . $file, $target . '/' . $file)) { + return false; + } + } + } + closedir($dir); + $this->remove($source); + return true; + } else { + if (is_array($stat1)) { + $parent = $this->stat(dirname($target)); + if (is_array($parent)) { + $this->remove($target); + $stat1['parent'] = $parent['fileid']; + $stat1['path'] = $target; + $stat1['path_hash'] = md5($target); + $stat1['name'] = \OC_Util::basename($target); + $stat1['mtime'] = time(); + $stat1['etag'] = $this->getETag($target); + $this->getCache()->update($stat1['fileid'], $stat1); + return true; + } + } + } + return false; + } + + public function getMimeType($path) { + $path = $this->normalizePath($path); + $stat = $this->stat($path); + if (is_array($stat)) { + return $stat['mimetype']; + } else { + return false; + } + } + + public function touch($path, $mtime = null) { + if (is_null($mtime)) { + $mtime = time(); + } + + $path = $this->normalizePath($path); + $dirName = dirname($path); + $parentExists = $this->is_dir($dirName); + if (!$parentExists) { + return false; + } + + $stat = $this->stat($path); + if (is_array($stat)) { + // update existing mtime in db + $stat['mtime'] = $mtime; + $this->getCache()->update($stat['fileid'], $stat); + } else { + $mimeType = \OC_Helper::getFileNameMimeType($path); + // create new file + $stat = array( + 'etag' => $this->getETag($path), + 'mimetype' => $mimeType, + 'size' => 0, + 'mtime' => $mtime, + 'storage_mtime' => $mtime, + 'permissions' => \OCP\PERMISSION_ALL, + ); + $fileId = $this->getCache()->put($path, $stat); + try { + //read an empty file from memory + $this->objectStore->writeObject($this->getURN($fileId), fopen('php://memory', 'r')); + } catch (\Exception $ex) { + $this->getCache()->remove($path); + \OCP\Util::writeLog('objectstore', 'Could not create object: ' . $ex->getMessage(), \OCP\Util::ERROR); + return false; + } + } + return true; + } + + public function writeBack($tmpFile) { + if (!isset(self::$tmpFiles[$tmpFile])) { + return false; + } + + $path = self::$tmpFiles[$tmpFile]; + $stat = $this->stat($path); + if (empty($stat)) { + // create new file + $stat = array( + 'etag' => $this->getETag($path), + 'permissions' => \OCP\PERMISSION_ALL, + ); + } + // update stat with new data + $mTime = time(); + $stat['size'] = filesize($tmpFile); + $stat['mtime'] = $mTime; + $stat['storage_mtime'] = $mTime; + $stat['mimetype'] = \OC_Helper::getMimeType($tmpFile); + + $fileId = $this->getCache()->put($path, $stat); + try { + //upload to object storage + $this->objectStore->writeObject($this->getURN($fileId), fopen($tmpFile, 'r')); + } catch (\Exception $ex) { + $this->getCache()->remove($path); + \OCP\Util::writeLog('objectstore', 'Could not create object: ' . $ex->getMessage(), \OCP\Util::ERROR); + return false; + } + return true; + } + + /** + * external changes are not supported, exclusive access to the object storage is assumed + * + * @param string $path + * @param int $time + * @return false + */ + public function hasUpdated($path, $time) { + return false; + } +} diff --git a/lib/private/files/objectstore/swift.php b/lib/private/files/objectstore/swift.php new file mode 100644 index 00000000000..3378fd7b86f --- /dev/null +++ b/lib/private/files/objectstore/swift.php @@ -0,0 +1,147 @@ +<?php +/** + * @author Jörn Friedrich Dreyer + * @copyright (c) 2014 Jörn Friedrich Dreyer <jfd@owncloud.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OC\Files\ObjectStore; + +use Guzzle\Http\Exception\ClientErrorResponseException; +use OCP\Files\ObjectStore\IObjectStore; +use OpenCloud\OpenStack; +use OpenCloud\Rackspace; + +class Swift implements IObjectStore { + /** + * @var \OpenCloud\OpenStack + */ + private $client; + + /** + * @var array + */ + private $params; + + /** + * @var \OpenCloud\ObjectStore\Service + */ + private $objectStoreService; + + /** + * @var \OpenCloud\ObjectStore\Resource\Container + */ + private $container; + + public function __construct($params) { + if (!isset($params['container'])) { + $params['container'] = 'owncloud'; + } + if (!isset($params['autocreate'])) { + // should only be true for tests + $params['autocreate'] = false; + } + + if (isset($params['apiKey'])) { + $this->client = new Rackspace($params['url'], $params); + } else { + $this->client = new OpenStack($params['url'], $params); + } + $this->params = $params; + } + + protected function init() { + if ($this->container) { + return; + } + + // the OpenCloud client library will default to 'cloudFiles' if $serviceName is null + $serviceName = null; + if ($this->params['serviceName']) { + $serviceName = $this->params['serviceName']; + } + + $this->objectStoreService = $this->client->objectStoreService($serviceName, $this->params['region']); + + try { + $this->container = $this->objectStoreService->getContainer($this->params['container']); + } catch (ClientErrorResponseException $ex) { + // if the container does not exist and autocreate is true try to create the container on the fly + if (isset($this->params['autocreate']) && $this->params['autocreate'] === true) { + $this->container = $this->objectStoreService->createContainer($this->params['container']); + } else { + throw $ex; + } + } + } + + /** + * @return string the container name where objects are stored + */ + public function getStorageId() { + return $this->params['container']; + } + + /** + * @param string $urn the unified resource name used to identify the object + * @param resource $stream stream with the data to write + * @throws Exception from openstack lib when something goes wrong + */ + public function writeObject($urn, $stream) { + $this->init(); + $this->container->uploadObject($urn, $stream); + } + + /** + * @param string $urn the unified resource name used to identify the object + * @return resource stream with the read data + * @throws Exception from openstack lib when something goes wrong + */ + public function readObject($urn) { + $this->init(); + $object = $this->container->getObject($urn); + + // we need to keep a reference to objectContent or + // the stream will be closed before we can do anything with it + /** @var $objectContent \Guzzle\Http\EntityBody * */ + $objectContent = $object->getContent(); + $objectContent->rewind(); + + // directly returning the object stream does not work because the GC seems to collect it, so we need a copy + $tmpStream = fopen('php://temp', 'r+'); + stream_copy_to_stream($objectContent->getStream(), $tmpStream); + rewind($tmpStream); + + return $tmpStream; + } + + /** + * @param string $urn Unified Resource Name + * @return void + * @throws Exception from openstack lib when something goes wrong + */ + public function deleteObject($urn) { + $this->init(); + // see https://github.com/rackspace/php-opencloud/issues/243#issuecomment-30032242 + $this->container->dataObject()->setName($urn)->delete(); + } + + public function deleteContainer($recursive = false) { + $this->init(); + $this->container->delete($recursive); + } + +} diff --git a/lib/private/files/storage/common.php b/lib/private/files/storage/common.php index ecc75298b66..9657b511f15 100644 --- a/lib/private/files/storage/common.php +++ b/lib/private/files/storage/common.php @@ -279,6 +279,11 @@ abstract class Common implements \OC\Files\Storage\Storage { /** * check if a file or folder has been updated since $time * + * The method is only used to check if the cache needs to be updated. Storage backends that don't support checking + * the mtime should always return false here. As a result storage implementations that always return false expect + * exclusive access to the backend and will not pick up files that have been added in a way that circumvents + * ownClouds filesystem. + * * @param string $path * @param int $time * @return bool diff --git a/lib/private/files/storage/dav.php b/lib/private/files/storage/dav.php index 8536c65ace9..8b97f750204 100644 --- a/lib/private/files/storage/dav.php +++ b/lib/private/files/storage/dav.php @@ -426,21 +426,7 @@ class DAV extends \OC\Files\Storage\Common { $this->init(); $response = $this->client->propfind($this->encodePath($path), array('{http://owncloud.org/ns}permissions')); if (isset($response['{http://owncloud.org/ns}permissions'])) { - $permissions = \OCP\PERMISSION_READ; - $permissionsString = $response['{http://owncloud.org/ns}permissions']; - if (strpos($permissionsString, 'R') !== false) { - $permissions |= \OCP\PERMISSION_SHARE; - } - if (strpos($permissionsString, 'D') !== false) { - $permissions |= \OCP\PERMISSION_DELETE; - } - if (strpos($permissionsString, 'W') !== false) { - $permissions |= \OCP\PERMISSION_UPDATE; - } - if (strpos($permissionsString, 'C') !== false) { - $permissions |= \OCP\PERMISSION_CREATE; - } - return $permissions; + return $this->parsePermissions($response['{http://owncloud.org/ns}permissions']); } else if ($this->is_dir($path)) { return \OCP\PERMISSION_ALL; } else if ($this->file_exists($path)) { @@ -449,5 +435,58 @@ class DAV extends \OC\Files\Storage\Common { return 0; } } + + /** + * @param string $permissionsString + * @return int + */ + protected function parsePermissions($permissionsString) { + $permissions = \OCP\PERMISSION_READ; + if (strpos($permissionsString, 'R') !== false) { + $permissions |= \OCP\PERMISSION_SHARE; + } + if (strpos($permissionsString, 'D') !== false) { + $permissions |= \OCP\PERMISSION_DELETE; + } + if (strpos($permissionsString, 'W') !== false) { + $permissions |= \OCP\PERMISSION_UPDATE; + } + if (strpos($permissionsString, 'CK') !== false) { + $permissions |= \OCP\PERMISSION_CREATE; + $permissions |= \OCP\PERMISSION_UPDATE; + } + return $permissions; + } + + /** + * check if a file or folder has been updated since $time + * + * @param string $path + * @param int $time + * @return bool + */ + public function hasUpdated($path, $time) { + $this->init(); + $response = $this->client->propfind($this->encodePath($path), array( + '{DAV:}getlastmodified', + '{DAV:}getetag', + '{http://owncloud.org/ns}permissions' + )); + if (isset($response['{DAV:}getetag'])) { + $cachedData = $this->getCache()->get($path); + $etag = trim($response['{DAV:}getetag'], '"'); + if ($cachedData['etag'] !== $etag) { + return true; + } else if (isset($response['{http://owncloud.org/ns}permissions'])) { + $permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']); + return $permissions !== $cachedData['permissions']; + } else { + return false; + } + } else { + $remoteMtime = strtotime($response['{DAV:}getlastmodified']); + return $remoteMtime > $time; + } + } } diff --git a/lib/private/util.php b/lib/private/util.php index 94005daef61..7836489832d 100755 --- a/lib/private/util.php +++ b/lib/private/util.php @@ -12,6 +12,45 @@ class OC_Util { private static $rootMounted=false; private static $fsSetup=false; + private static function initLocalStorageRootFS() { + // mount local file backend as root + $configDataDirectory = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); + //first set up the local "root" storage + \OC\Files\Filesystem::initMounts(); + if(!self::$rootMounted) { + \OC\Files\Filesystem::mount('\OC\Files\Storage\Local', array('datadir'=>$configDataDirectory), '/'); + self::$rootMounted = true; + } + } + + /** + * mounting an object storage as the root fs will in essence remove the + * necessity of a data folder being present. + * TODO make home storage aware of this and use the object storage instead of local disk access + * @param array $config containing 'class' and optional 'arguments' + */ + private static function initObjectStoreRootFS($config) { + // check misconfiguration + if (empty($config['class'])) { + \OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR); + } + if (!isset($config['arguments'])) { + $config['arguments'] = array(); + } + + // instantiate object store implementation + $config['arguments']['objectstore'] = new $config['class']($config['arguments']); + // mount with plain / root object store implementation + $config['class'] = '\OC\Files\ObjectStore\ObjectStoreStorage'; + + // mount object storage as root + \OC\Files\Filesystem::initMounts(); + if(!self::$rootMounted) { + \OC\Files\Filesystem::mount($config['class'], $config['arguments'], '/'); + self::$rootMounted = true; + } + } + /** * Can be set up * @param string $user @@ -39,12 +78,12 @@ class OC_Util { self::$fsSetup=true; } - $configDataDirectory = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); - //first set up the local "root" storage - \OC\Files\Filesystem::initMounts(); - if(!self::$rootMounted) { - \OC\Files\Filesystem::mount('\OC\Files\Storage\Local', array('datadir'=>$configDataDirectory), '/'); - self::$rootMounted = true; + //check if we are using an object storage + $objectStore = OC_Config::getValue( 'objectstore' ); + if ( isset( $objectStore ) ) { + self::initObjectStoreRootFS($objectStore); + } else { + self::initLocalStorageRootFS(); } if ($user != '' && !OCP\User::userExists($user)) { @@ -60,24 +99,33 @@ class OC_Util { /** * @var \OC\Files\Storage\Storage $storage */ - if ($storage->instanceOfStorage('\OC\Files\Storage\Home')) { - $user = $storage->getUser()->getUID(); - $quota = OC_Util::getUserQuota($user); - if ($quota !== \OC\Files\SPACE_UNLIMITED) { - return new \OC\Files\Storage\Wrapper\Quota(array('storage' => $storage, 'quota' => $quota, 'root' => 'files')); + if ($storage->instanceOfStorage('\OC\Files\Storage\Home') + || $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage') + ) { + if (is_object($storage->getUser())) { + $user = $storage->getUser()->getUID(); + $quota = OC_Util::getUserQuota($user); + if ($quota !== \OC\Files\SPACE_UNLIMITED) { + return new \OC\Files\Storage\Wrapper\Quota(array('storage' => $storage, 'quota' => $quota, 'root' => 'files')); + } } } return $storage; }); - $userDir = '/'.$user.'/files'; - $userRoot = OC_User::getHome($user); - $userDirectory = $userRoot . '/files'; - if( !is_dir( $userDirectory )) { - mkdir( $userDirectory, 0755, true ); - OC_Util::copySkeleton($userDirectory); + // copy skeleton for local storage only + if ( ! isset( $objectStore ) ) { + $userRoot = OC_User::getHome($user); + $userDirectory = $userRoot . '/files'; + if( !is_dir( $userDirectory )) { + mkdir( $userDirectory, 0755, true ); + OC_Util::copySkeleton($userDirectory); + } } + + $userDir = '/'.$user.'/files'; + //jail the user into his "home" directory \OC\Files\Filesystem::init($user, $userDir); @@ -326,6 +374,7 @@ class OC_Util { * @return array arrays with error messages and hints */ public static function checkServer() { + $l = OC_L10N::get('lib'); $errors = array(); $CONFIG_DATADIRECTORY = OC_Config::getValue('datadirectory', OC::$SERVERROOT . '/data'); @@ -346,24 +395,24 @@ class OC_Util { and !is_callable('pg_connect') and !is_callable('oci_connect')) { $errors[] = array( - 'error'=>'No database drivers (sqlite, mysql, or postgresql) installed.', + 'error'=> $l->t('No database drivers (sqlite, mysql, or postgresql) installed.'), 'hint'=>'' //TODO: sane hint ); $webServerRestart = true; } //common hint for all file permissions error messages - $permissionsHint = 'Permissions can usually be fixed by ' - .'<a href="' . OC_Helper::linkToDocs('admin-dir_permissions') - .'" target="_blank">giving the webserver write access to the root directory</a>.'; + $permissionsHint = $l->t('Permissions can usually be fixed by ' + .'%sgiving the webserver write access to the root directory%s.', + array('<a href="'.\OC_Helper::linkToDocs('admin-dir_permissions').'" target="_blank">', '</a>')); // Check if config folder is writable. if(!is_writable(OC::$configDir) or !is_readable(OC::$configDir)) { $errors[] = array( - 'error' => "Can't write into config directory", - 'hint' => 'This can usually be fixed by ' - .'<a href="' . OC_Helper::linkToDocs('admin-dir_permissions') - .'" target="_blank">giving the webserver write access to the config directory</a>.' + 'error' => $l->t('Cannot write into "config" directory'), + 'hint' => $l->t('This can usually be fixed by ' + .'%sgiving the webserver write access to the config directory%s.', + array('<a href="'.\OC_Helper::linkToDocs('admin-dir_permissions').'" target="_blank">', '</a>')) ); } @@ -373,11 +422,11 @@ class OC_Util { || !is_writable(OC_App::getInstallPath()) || !is_readable(OC_App::getInstallPath()) ) { $errors[] = array( - 'error' => "Can't write into apps directory", - 'hint' => 'This can usually be fixed by ' - .'<a href="' . OC_Helper::linkToDocs('admin-dir_permissions') - .'" target="_blank">giving the webserver write access to the apps directory</a> ' - .'or disabling the appstore in the config file.' + 'error' => $l->t('Cannot write into "apps" directory'), + 'hint' => $l->t('This can usually be fixed by ' + .'%sgiving the webserver write access to the apps directory%s' + .' or disabling the appstore in the config file.', + array('<a href="'.\OC_Helper::linkToDocs('admin-dir_permissions').'" target="_blank">', '</a>')) ); } } @@ -388,11 +437,11 @@ class OC_Util { $errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY)); } else { $errors[] = array( - 'error' => "Can't create data directory (".$CONFIG_DATADIRECTORY.")", - 'hint' => 'This can usually be fixed by ' - .'<a href="' . OC_Helper::linkToDocs('admin-dir_permissions') - .'" target="_blank">giving the webserver write access to the root directory</a>.' - ); + 'error' => $l->t('Cannot create "data" directory (%s)', array($CONFIG_DATADIRECTORY)), + 'hint' => $l->t('This can usually be fixed by ' + .'<a href="%s" target="_blank">giving the webserver write access to the root directory</a>.', + array(OC_Helper::linkToDocs('admin-dir_permissions'))) + ); } } else if(!is_writable($CONFIG_DATADIRECTORY) or !is_readable($CONFIG_DATADIRECTORY)) { $errors[] = array( @@ -405,30 +454,32 @@ class OC_Util { if(!OC_Util::isSetLocaleWorking()) { $errors[] = array( - 'error' => 'Setting locale to en_US.UTF-8/fr_FR.UTF-8/es_ES.UTF-8/de_DE.UTF-8/ru_RU.UTF-8/pt_BR.UTF-8/it_IT.UTF-8/ja_JP.UTF-8/zh_CN.UTF-8 failed', - 'hint' => 'Please install one of theses locales on your system and restart your webserver.' + 'error' => $l->t('Setting locale to %s failed', + array('en_US.UTF-8/fr_FR.UTF-8/es_ES.UTF-8/de_DE.UTF-8/ru_RU.UTF-8/' + .'pt_BR.UTF-8/it_IT.UTF-8/ja_JP.UTF-8/zh_CN.UTF-8')), + 'hint' => $l->t('Please install one of theses locales on your system and restart your webserver.') ); } - $moduleHint = "Please ask your server administrator to install the module."; + $moduleHint = $l->t('Please ask your server administrator to install the module.'); // check if all required php modules are present if(!class_exists('ZipArchive')) { $errors[] = array( - 'error'=>'PHP module zip not installed.', + 'error'=> $l->t('PHP module %s not installed.', array('zip')), 'hint'=>$moduleHint ); $webServerRestart = true; } if(!class_exists('DOMDocument')) { $errors[] = array( - 'error' => 'PHP module dom not installed.', + 'error'=> $l->t('PHP module %s not installed.', array('dom')), 'hint' => $moduleHint ); $webServerRestart =true; } if(!function_exists('xml_parser_create')) { $errors[] = array( - 'error' => 'PHP module libxml not installed.', + 'error'=> $l->t('PHP module %s not installed.', array('libxml')), 'hint' => $moduleHint ); $webServerRestart = true; @@ -442,57 +493,57 @@ class OC_Util { } if(!function_exists('ctype_digit')) { $errors[] = array( - 'error'=>'PHP module ctype is not installed.', + 'error'=> $l->t('PHP module %s not installed.', array('ctype')), 'hint'=>$moduleHint ); $webServerRestart = true; } if(!function_exists('json_encode')) { $errors[] = array( - 'error'=>'PHP module JSON is not installed.', + 'error'=> $l->t('PHP module %s not installed.', array('JSON')), 'hint'=>$moduleHint ); $webServerRestart = true; } if(!extension_loaded('gd') || !function_exists('gd_info')) { $errors[] = array( - 'error'=>'PHP module GD is not installed.', + 'error'=> $l->t('PHP module %s not installed.', array('GD')), 'hint'=>$moduleHint ); $webServerRestart = true; } if(!function_exists('gzencode')) { $errors[] = array( - 'error'=>'PHP module zlib is not installed.', + 'error'=> $l->t('PHP module %s not installed.', array('zlib')), 'hint'=>$moduleHint ); $webServerRestart = true; } if(!function_exists('iconv')) { $errors[] = array( - 'error'=>'PHP module iconv is not installed.', + 'error'=> $l->t('PHP module %s not installed.', array('iconv')), 'hint'=>$moduleHint ); $webServerRestart = true; } if(!function_exists('simplexml_load_string')) { $errors[] = array( - 'error'=>'PHP module SimpleXML is not installed.', + 'error'=> $l->t('PHP module %s not installed.', array('SimpleXML')), 'hint'=>$moduleHint ); $webServerRestart = true; } if(version_compare(phpversion(), '5.3.3', '<')) { $errors[] = array( - 'error'=>'PHP 5.3.3 or higher is required.', - 'hint'=>'Please ask your server administrator to update PHP to the latest version.' - .' Your PHP version is no longer supported by ownCloud and the PHP community.' + 'error'=> $l->t('PHP %s or higher is required.', '5.3.3'), + 'hint'=> $l->t('Please ask your server administrator to update PHP to the latest version.' + .' Your PHP version is no longer supported by ownCloud and the PHP community.') ); $webServerRestart = true; } if(!defined('PDO::ATTR_DRIVER_NAME')) { $errors[] = array( - 'error'=>'PHP PDO module is not installed.', + 'error'=> $l->t('PHP module %s not installed.', array('PDO')), 'hint'=>$moduleHint ); $webServerRestart = true; @@ -502,17 +553,17 @@ class OC_Util { || (strtolower(@ini_get('safe_mode')) == 'true') || (ini_get("safe_mode") == 1 ))) { $errors[] = array( - 'error'=>'PHP Safe Mode is enabled. ownCloud requires that it is disabled to work properly.', - 'hint'=>'PHP Safe Mode is a deprecated and mostly useless setting that should be disabled. ' - .'Please ask your server administrator to disable it in php.ini or in your webserver config.' + 'error'=> $l->t('PHP Safe Mode is enabled. ownCloud requires that it is disabled to work properly.'), + 'hint'=> $l->t('PHP Safe Mode is a deprecated and mostly useless setting that should be disabled. ' + .'Please ask your server administrator to disable it in php.ini or in your webserver config.') ); $webServerRestart = true; } if (get_magic_quotes_gpc() == 1 ) { $errors[] = array( - 'error'=>'Magic Quotes is enabled. ownCloud requires that it is disabled to work properly.', - 'hint'=>'Magic Quotes is a deprecated and mostly useless setting that should be disabled. ' - .'Please ask your server administrator to disable it in php.ini or in your webserver config.' + 'error'=> $l->t('Magic Quotes is enabled. ownCloud requires that it is disabled to work properly.'), + 'hint'=> $l->t('Magic Quotes is a deprecated and mostly useless setting that should be disabled. ' + .'Please ask your server administrator to disable it in php.ini or in your webserver config.') ); $webServerRestart = true; } @@ -525,8 +576,8 @@ class OC_Util { if($webServerRestart) { $errors[] = array( - 'error'=>'PHP modules have been installed, but they are still listed as missing?', - 'hint'=>'Please ask your server administrator to restart the web server.' + 'error'=> $l->t('PHP modules have been installed, but they are still listed as missing?'), + 'hint'=> $l->t('Please ask your server administrator to restart the web server.') ); } @@ -543,6 +594,7 @@ class OC_Util { * @return array errors array */ public static function checkDatabaseVersion() { + $l = OC_L10N::get('lib'); $errors = array(); $dbType = \OC_Config::getValue('dbtype', 'sqlite'); if ($dbType === 'pgsql') { @@ -554,16 +606,17 @@ class OC_Util { $version = $data['server_version']; if (version_compare($version, '9.0.0', '<')) { $errors[] = array( - 'error' => 'PostgreSQL >= 9 required', - 'hint' => 'Please upgrade your database version' + 'error' => $l->t('PostgreSQL >= 9 required'), + 'hint' => $l->t('Please upgrade your database version') ); } } } catch (\Doctrine\DBAL\DBALException $e) { \OCP\Util::logException('core', $e); $errors[] = array( - 'error' => 'Error occurred while checking PostgreSQL version', - 'hint' => 'Please make sure you have PostgreSQL >= 9 or check the logs for more information about the error' + 'error' => $l->t('Error occurred while checking PostgreSQL version'), + 'hint' => $l->t('Please make sure you have PostgreSQL >= 9 or' + .' check the logs for more information about the error') ); } } @@ -619,12 +672,13 @@ class OC_Util { * @return array arrays with error messages and hints */ public static function checkDataDirectoryPermissions($dataDirectory) { + $l = OC_L10N::get('lib'); $errors = array(); if (self::runningOnWindows()) { //TODO: permissions checks for windows hosts } else { - $permissionsModHint = 'Please change the permissions to 0770 so that the directory' - .' cannot be listed by other users.'; + $permissionsModHint = $l->t('Please change the permissions to 0770 so that the directory' + .' cannot be listed by other users.'); $perms = substr(decoct(@fileperms($dataDirectory)), -3); if (substr($perms, -1) != '0') { chmod($dataDirectory, 0770); @@ -632,7 +686,7 @@ class OC_Util { $perms = substr(decoct(@fileperms($dataDirectory)), -3); if (substr($perms, 2, 1) != '0') { $errors[] = array( - 'error' => 'Data directory ('.$dataDirectory.') is readable for other users', + 'error' => $l->t('Data directory (%s) is readable by other users', array($dataDirectory)), 'hint' => $permissionsModHint ); } @@ -649,12 +703,13 @@ class OC_Util { * @return bool true if the data directory is valid, false otherwise */ public static function checkDataDirectoryValidity($dataDirectory) { + $l = OC_L10N::get('lib'); $errors = array(); if (!file_exists($dataDirectory.'/.ocdata')) { $errors[] = array( - 'error' => 'Data directory (' . $dataDirectory . ') is invalid', - 'hint' => 'Please check that the data directory contains a file' . - ' ".ocdata" in its root.' + 'error' => $l->t('Data directory (%s) is invalid', array($dataDirectory)), + 'hint' => $l->t('Please check that the data directory contains a file' . + ' ".ocdata" in its root.') ); } return $errors; diff --git a/lib/public/files/objectstore/iobjectstore.php b/lib/public/files/objectstore/iobjectstore.php new file mode 100644 index 00000000000..b2c5a9da134 --- /dev/null +++ b/lib/public/files/objectstore/iobjectstore.php @@ -0,0 +1,33 @@ +<?php + +namespace OCP\Files\ObjectStore; + +interface IObjectStore { + + /** + * @return string the container or bucket name where objects are stored + */ + function getStorageId(); + + /** + * @param string $urn the unified resource name used to identify the object + * @return resource stream with the read data + * @throws Exception when something goes wrong, message will be logged + */ + function readObject($urn); + + /** + * @param string $urn the unified resource name used to identify the object + * @param resource $stream stream with the data to write + * @throws Exception when something goes wrong, message will be logged + */ + function writeObject($urn, $stream); + + /** + * @param string $urn the unified resource name used to identify the object + * @return void + * @throws Exception when something goes wrong, message will be logged + */ + function deleteObject($urn); + +}
\ No newline at end of file |