summaryrefslogtreecommitdiffstats
path: root/lib/private/connector/sabre
diff options
context:
space:
mode:
authorThomas Müller <thomas.mueller@tmit.eu>2013-10-04 14:06:42 +0200
committerThomas Müller <thomas.mueller@tmit.eu>2013-10-04 14:06:42 +0200
commitaebc330f269f3e88f2891cd13009cde2d50eb59a (patch)
tree7398bc68d581d01b18ba3e9e79c82491f1ecf871 /lib/private/connector/sabre
parentc62dc4fa80d622ed7651029e9ddff2a6c6327143 (diff)
parent800bf0769ff4ea63c5dcc77eaaf1bce669b73d13 (diff)
downloadnextcloud-server-aebc330f269f3e88f2891cd13009cde2d50eb59a.tar.gz
nextcloud-server-aebc330f269f3e88f2891cd13009cde2d50eb59a.zip
Merge branch 'master' into fixing-4011-master
Diffstat (limited to 'lib/private/connector/sabre')
-rw-r--r--lib/private/connector/sabre/ServiceUnavailable.php22
-rw-r--r--lib/private/connector/sabre/aborteduploaddetectionplugin.php101
-rw-r--r--lib/private/connector/sabre/auth.php84
-rw-r--r--lib/private/connector/sabre/directory.php218
-rw-r--r--lib/private/connector/sabre/file.php192
-rw-r--r--lib/private/connector/sabre/locks.php189
-rw-r--r--lib/private/connector/sabre/maintenanceplugin.php59
-rw-r--r--lib/private/connector/sabre/node.php258
-rw-r--r--lib/private/connector/sabre/objecttree.php142
-rw-r--r--lib/private/connector/sabre/principal.php128
-rw-r--r--lib/private/connector/sabre/quotaplugin.php97
-rw-r--r--lib/private/connector/sabre/request.php50
12 files changed, 1540 insertions, 0 deletions
diff --git a/lib/private/connector/sabre/ServiceUnavailable.php b/lib/private/connector/sabre/ServiceUnavailable.php
new file mode 100644
index 00000000000..c1cc815c989
--- /dev/null
+++ b/lib/private/connector/sabre/ServiceUnavailable.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Thomas Müller
+ * @copyright 2013 Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @license AGPL3
+ */
+
+class Sabre_DAV_Exception_ServiceUnavailable extends Sabre_DAV_Exception {
+
+ /**
+ * Returns the HTTP statuscode for this exception
+ *
+ * @return int
+ */
+ public function getHTTPCode() {
+
+ return 503;
+ }
+}
diff --git a/lib/private/connector/sabre/aborteduploaddetectionplugin.php b/lib/private/connector/sabre/aborteduploaddetectionplugin.php
new file mode 100644
index 00000000000..15dca3a6809
--- /dev/null
+++ b/lib/private/connector/sabre/aborteduploaddetectionplugin.php
@@ -0,0 +1,101 @@
+<?php
+/**
+ * Copyright (c) 2013 Thomas Müller <thomas.mueller@tmit.eu>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+/**
+ * Class OC_Connector_Sabre_AbortedUploadDetectionPlugin
+ *
+ * This plugin will verify if the uploaded data has been stored completely.
+ * This is done by comparing the content length of the request with the file size on storage.
+ */
+class OC_Connector_Sabre_AbortedUploadDetectionPlugin extends Sabre_DAV_ServerPlugin {
+
+ /**
+ * Reference to main server object
+ *
+ * @var Sabre_DAV_Server
+ */
+ private $server;
+
+ /**
+ * is kept public to allow overwrite for unit testing
+ *
+ * @var \OC\Files\View
+ */
+ public $fileView;
+
+ /**
+ * This initializes the plugin.
+ *
+ * This function is called by Sabre_DAV_Server, after
+ * addPlugin is called.
+ *
+ * This method should set up the requires event subscriptions.
+ *
+ * @param Sabre_DAV_Server $server
+ */
+ public function initialize(Sabre_DAV_Server $server) {
+
+ $this->server = $server;
+
+ $server->subscribeEvent('afterCreateFile', array($this, 'verifyContentLength'), 10);
+ $server->subscribeEvent('afterWriteContent', array($this, 'verifyContentLength'), 10);
+ }
+
+ /**
+ * @param $filePath
+ * @param Sabre_DAV_INode $node
+ * @throws Sabre_DAV_Exception_BadRequest
+ */
+ public function verifyContentLength($filePath, Sabre_DAV_INode $node = null) {
+
+ // ownCloud chunked upload will be handled in its own plugin
+ $chunkHeader = $this->server->httpRequest->getHeader('OC-Chunked');
+ if ($chunkHeader) {
+ return;
+ }
+
+ // compare expected and actual size
+ $expected = $this->getLength();
+ if (!$expected) {
+ return;
+ }
+ $actual = $this->getFileView()->filesize($filePath);
+ if ($actual != $expected) {
+ $this->getFileView()->unlink($filePath);
+ throw new Sabre_DAV_Exception_BadRequest('expected filesize ' . $expected . ' got ' . $actual);
+ }
+
+ }
+
+ /**
+ * @return string
+ */
+ public function getLength()
+ {
+ $req = $this->server->httpRequest;
+ $length = $req->getHeader('X-Expected-Entity-Length');
+ if (!$length) {
+ $length = $req->getHeader('Content-Length');
+ }
+
+ return $length;
+ }
+
+ /**
+ * @return \OC\Files\View
+ */
+ public function getFileView()
+ {
+ if (is_null($this->fileView)) {
+ // initialize fileView
+ $this->fileView = \OC\Files\Filesystem::getView();
+ }
+
+ return $this->fileView;
+ }
+}
diff --git a/lib/private/connector/sabre/auth.php b/lib/private/connector/sabre/auth.php
new file mode 100644
index 00000000000..bf3a49593cb
--- /dev/null
+++ b/lib/private/connector/sabre/auth.php
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack kde@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+
+class OC_Connector_Sabre_Auth extends Sabre_DAV_Auth_Backend_AbstractBasic {
+ /**
+ * Validates a username and password
+ *
+ * This method should return true or false depending on if login
+ * succeeded.
+ *
+ * @return bool
+ */
+ protected function validateUserPass($username, $password) {
+ if (OC_User::isLoggedIn()) {
+ OC_Util::setupFS(OC_User::getUser());
+ return true;
+ } else {
+ OC_Util::setUpFS();//login hooks may need early access to the filesystem
+ if(OC_User::login($username, $password)) {
+ OC_Util::setUpFS(OC_User::getUser());
+ return true;
+ }
+ else{
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Returns information about the currently logged in username.
+ *
+ * If nobody is currently logged in, this method should return null.
+ *
+ * @return string|null
+ */
+ public function getCurrentUser() {
+ $user = OC_User::getUser();
+ if(!$user) {
+ return null;
+ }
+ return $user;
+ }
+
+ /**
+ * Override function here. We want to cache authentication cookies
+ * in the syncing client to avoid HTTP-401 roundtrips.
+ * If the sync client supplies the cookies, then OC_User::isLoggedIn()
+ * will return true and we can see this WebDAV request as already authenticated,
+ * even if there are no HTTP Basic Auth headers.
+ * In other case, just fallback to the parent implementation.
+ *
+ * @return bool
+ */
+ public function authenticate(Sabre_DAV_Server $server, $realm) {
+ if (OC_User::isLoggedIn()) {
+ $user = OC_User::getUser();
+ OC_Util::setupFS($user);
+ $this->currentUser = $user;
+ return true;
+ }
+
+ return parent::authenticate($server, $realm);
+ }
+}
diff --git a/lib/private/connector/sabre/directory.php b/lib/private/connector/sabre/directory.php
new file mode 100644
index 00000000000..af0dfd70f08
--- /dev/null
+++ b/lib/private/connector/sabre/directory.php
@@ -0,0 +1,218 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack kde@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+
+class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sabre_DAV_ICollection, Sabre_DAV_IQuota {
+
+ /**
+ * Creates a new file in the directory
+ *
+ * Data will either be supplied as a stream resource, or in certain cases
+ * as a string. Keep in mind that you may have to support either.
+ *
+ * After succesful creation of the file, you may choose to return the ETag
+ * of the new file here.
+ *
+ * The returned ETag must be surrounded by double-quotes (The quotes should
+ * be part of the actual string).
+ *
+ * If you cannot accurately determine the ETag, you should not return it.
+ * If you don't store the file exactly as-is (you're transforming it
+ * somehow) you should also not return an ETag.
+ *
+ * This means that if a subsequent GET to this new file does not exactly
+ * return the same contents of what was submitted here, you are strongly
+ * recommended to omit the ETag.
+ *
+ * @param string $name Name of the file
+ * @param resource|string $data Initial payload
+ * @throws Sabre_DAV_Exception_Forbidden
+ * @return null|string
+ */
+ public function createFile($name, $data = null) {
+
+ if (!\OC\Files\Filesystem::isCreatable($this->path)) {
+ throw new \Sabre_DAV_Exception_Forbidden();
+ }
+
+ $path = $this->path . '/' . $name;
+ $node = new OC_Connector_Sabre_File($path);
+ return $node->put($data);
+
+ }
+
+ /**
+ * Creates a new subdirectory
+ *
+ * @param string $name
+ * @throws Sabre_DAV_Exception_Forbidden
+ * @return void
+ */
+ public function createDirectory($name) {
+
+ if (!\OC\Files\Filesystem::isCreatable($this->path)) {
+ throw new \Sabre_DAV_Exception_Forbidden();
+ }
+
+ $newPath = $this->path . '/' . $name;
+ if(!\OC\Files\Filesystem::mkdir($newPath)) {
+ throw new Sabre_DAV_Exception_Forbidden('Could not create directory '.$newPath);
+ }
+
+ }
+
+ /**
+ * Returns a specific child node, referenced by its name
+ *
+ * @param string $name
+ * @throws Sabre_DAV_Exception_FileNotFound
+ * @return Sabre_DAV_INode
+ */
+ public function getChild($name, $info = null) {
+
+ $path = $this->path . '/' . $name;
+ if (is_null($info)) {
+ $info = \OC\Files\Filesystem::getFileInfo($path);
+ }
+
+ if (!$info) {
+ throw new Sabre_DAV_Exception_NotFound('File with name ' . $path . ' could not be located');
+ }
+
+ if ($info['mimetype'] == 'httpd/unix-directory') {
+ $node = new OC_Connector_Sabre_Directory($path);
+ } else {
+ $node = new OC_Connector_Sabre_File($path);
+ }
+
+ $node->setFileinfoCache($info);
+ return $node;
+ }
+
+ /**
+ * Returns an array with all the child nodes
+ *
+ * @return Sabre_DAV_INode[]
+ */
+ public function getChildren() {
+
+ $folder_content = \OC\Files\Filesystem::getDirectoryContent($this->path);
+ $paths = array();
+ foreach($folder_content as $info) {
+ $paths[] = $this->path.'/'.$info['name'];
+ $properties[$this->path.'/'.$info['name']][self::GETETAG_PROPERTYNAME] = '"' . $info['etag'] . '"';
+ }
+ if(count($paths)>0) {
+ //
+ // the number of arguments within IN conditions are limited in most databases
+ // we chunk $paths into arrays of 200 items each to meet this criteria
+ //
+ $chunks = array_chunk($paths, 200, false);
+ foreach ($chunks as $pack) {
+ $placeholders = join(',', array_fill(0, count($pack), '?'));
+ $query = OC_DB::prepare( 'SELECT * FROM `*PREFIX*properties`'
+ .' WHERE `userid` = ?' . ' AND `propertypath` IN ('.$placeholders.')' );
+ array_unshift($pack, OC_User::getUser()); // prepend userid
+ $result = $query->execute( $pack );
+ while($row = $result->fetchRow()) {
+ $propertypath = $row['propertypath'];
+ $propertyname = $row['propertyname'];
+ $propertyvalue = $row['propertyvalue'];
+ if($propertyname !== self::GETETAG_PROPERTYNAME) {
+ $properties[$propertypath][$propertyname] = $propertyvalue;
+ }
+ }
+ }
+ }
+
+ $nodes = array();
+ foreach($folder_content as $info) {
+ $node = $this->getChild($info['name'], $info);
+ $node->setPropertyCache($properties[$this->path.'/'.$info['name']]);
+ $nodes[] = $node;
+ }
+ return $nodes;
+ }
+
+ /**
+ * Checks if a child exists.
+ *
+ * @param string $name
+ * @return bool
+ */
+ public function childExists($name) {
+
+ $path = $this->path . '/' . $name;
+ return \OC\Files\Filesystem::file_exists($path);
+
+ }
+
+ /**
+ * Deletes all files in this directory, and then itself
+ *
+ * @return void
+ * @throws Sabre_DAV_Exception_Forbidden
+ */
+ public function delete() {
+
+ if (!\OC\Files\Filesystem::isDeletable($this->path)) {
+ throw new \Sabre_DAV_Exception_Forbidden();
+ }
+ if ($this->path != "/Shared") {
+ \OC\Files\Filesystem::rmdir($this->path);
+ }
+
+ }
+
+ /**
+ * Returns available diskspace information
+ *
+ * @return array
+ */
+ public function getQuotaInfo() {
+ $storageInfo = OC_Helper::getStorageInfo($this->path);
+ return array(
+ $storageInfo['used'],
+ $storageInfo['free']
+ );
+
+ }
+
+ /**
+ * Returns a list of properties for this nodes.;
+ *
+ * The properties list is a list of propertynames the client requested,
+ * encoded as xmlnamespace#tagName, for example:
+ * http://www.example.org/namespace#author
+ * If the array is empty, all properties should be returned
+ *
+ * @param array $properties
+ * @return array
+ */
+ public function getProperties($properties) {
+ $props = parent::getProperties($properties);
+ if (in_array(self::GETETAG_PROPERTYNAME, $properties) && !isset($props[self::GETETAG_PROPERTYNAME])) {
+ $props[self::GETETAG_PROPERTYNAME] = $this->getETagPropertyForPath($this->path);
+ }
+ return $props;
+ }
+}
diff --git a/lib/private/connector/sabre/file.php b/lib/private/connector/sabre/file.php
new file mode 100644
index 00000000000..8ffec371e3f
--- /dev/null
+++ b/lib/private/connector/sabre/file.php
@@ -0,0 +1,192 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack kde@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+
+class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_DAV_IFile {
+
+ /**
+ * Updates the data
+ *
+ * The data argument is a readable stream resource.
+ *
+ * After a successful put operation, you may choose to return an ETag. The
+ * etag must always be surrounded by double-quotes. These quotes must
+ * appear in the actual string you're returning.
+ *
+ * Clients may use the ETag from a PUT request to later on make sure that
+ * when they update the file, the contents haven't changed in the mean
+ * time.
+ *
+ * If you don't plan to store the file byte-by-byte, and you return a
+ * different object on a subsequent GET you are strongly recommended to not
+ * return an ETag, and just return null.
+ *
+ * @param resource $data
+ * @throws Sabre_DAV_Exception_Forbidden
+ * @return string|null
+ */
+ public function put($data) {
+ $fs = $this->getFS();
+ if ($fs->file_exists($this->path) &&
+ !$fs->isUpdatable($this->path)) {
+ throw new \Sabre_DAV_Exception_Forbidden();
+ }
+
+ // throw an exception if encryption was disabled but the files are still encrypted
+ if (\OC_Util::encryptedFiles()) {
+ throw new \Sabre_DAV_Exception_ServiceUnavailable();
+ }
+
+ // chunked handling
+ if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
+ list(, $name) = \Sabre_DAV_URLUtil::splitPath($this->path);
+
+ $info = OC_FileChunking::decodeName($name);
+ if (empty($info)) {
+ throw new Sabre_DAV_Exception_NotImplemented();
+ }
+ $chunk_handler = new OC_FileChunking($info);
+ $chunk_handler->store($info['index'], $data);
+ if ($chunk_handler->isComplete()) {
+ $newPath = $this->path . '/' . $info['name'];
+ $chunk_handler->file_assemble($newPath);
+ return $this->getETagPropertyForPath($newPath);
+ }
+
+ return null;
+ }
+
+ // mark file as partial while uploading (ignored by the scanner)
+ $partpath = $this->path . '.part';
+
+ try {
+ $putOkay = $fs->file_put_contents($partpath, $data);
+ if ($putOkay === false) {
+ \OC_Log::write('webdav', '\OC\Files\Filesystem::file_put_contents() failed', \OC_Log::ERROR);
+ $fs->unlink($partpath);
+ // because we have no clue about the cause we can only throw back a 500/Internal Server Error
+ throw new Sabre_DAV_Exception();
+ }
+ } catch (\OCP\Files\NotPermittedException $e) {
+ throw new Sabre_DAV_Exception_Forbidden();
+ }
+
+ // rename to correct path
+ $renameOkay = $fs->rename($partpath, $this->path);
+ $fileExists = $fs->file_exists($this->path);
+ if ($renameOkay === false || $fileExists === false) {
+ \OC_Log::write('webdav', '\OC\Files\Filesystem::rename() failed', \OC_Log::ERROR);
+ $fs->unlink($partpath);
+ throw new Sabre_DAV_Exception();
+ }
+
+ // allow sync clients to send the mtime along in a header
+ $mtime = OC_Request::hasModificationTime();
+ if ($mtime !== false) {
+ if($fs->touch($this->path, $mtime)) {
+ header('X-OC-MTime: accepted');
+ }
+ }
+
+ return $this->getETagPropertyForPath($this->path);
+ }
+
+ /**
+ * Returns the data
+ *
+ * @return string
+ */
+ public function get() {
+
+ //throw exception if encryption is disabled but files are still encrypted
+ if (\OC_Util::encryptedFiles()) {
+ throw new \Sabre_DAV_Exception_ServiceUnavailable();
+ } else {
+ return \OC\Files\Filesystem::fopen($this->path, 'rb');
+ }
+
+ }
+
+ /**
+ * Delete the current file
+ *
+ * @return void
+ * @throws Sabre_DAV_Exception_Forbidden
+ */
+ public function delete() {
+
+ if (!\OC\Files\Filesystem::isDeletable($this->path)) {
+ throw new \Sabre_DAV_Exception_Forbidden();
+ }
+ \OC\Files\Filesystem::unlink($this->path);
+
+ }
+
+ /**
+ * Returns the size of the node, in bytes
+ *
+ * @return int
+ */
+ public function getSize() {
+ $this->getFileinfoCache();
+ if ($this->fileinfo_cache['size'] > -1) {
+ return $this->fileinfo_cache['size'];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the ETag for a file
+ *
+ * An ETag is a unique identifier representing the current version of the
+ * file. If the file changes, the ETag MUST change. The ETag is an
+ * arbitrary string, but MUST be surrounded by double-quotes.
+ *
+ * Return null if the ETag can not effectively be determined
+ *
+ * @return mixed
+ */
+ public function getETag() {
+ $properties = $this->getProperties(array(self::GETETAG_PROPERTYNAME));
+ if (isset($properties[self::GETETAG_PROPERTYNAME])) {
+ return $properties[self::GETETAG_PROPERTYNAME];
+ }
+ return null;
+ }
+
+ /**
+ * Returns the mime-type for a file
+ *
+ * If null is returned, we'll assume application/octet-stream
+ *
+ * @return mixed
+ */
+ public function getContentType() {
+ if (isset($this->fileinfo_cache['mimetype'])) {
+ return $this->fileinfo_cache['mimetype'];
+ }
+
+ return \OC\Files\Filesystem::getMimeType($this->path);
+
+ }
+}
diff --git a/lib/private/connector/sabre/locks.php b/lib/private/connector/sabre/locks.php
new file mode 100644
index 00000000000..69496c15ada
--- /dev/null
+++ b/lib/private/connector/sabre/locks.php
@@ -0,0 +1,189 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack kde@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+
+class OC_Connector_Sabre_Locks extends Sabre_DAV_Locks_Backend_Abstract {
+
+ /**
+ * Returns a list of Sabre_DAV_Locks_LockInfo objects
+ *
+ * This method should return all the locks for a particular uri, including
+ * locks that might be set on a parent uri.
+ *
+ * If returnChildLocks is set to true, this method should also look for
+ * any locks in the subtree of the uri for locks.
+ *
+ * @param string $uri
+ * @param bool $returnChildLocks
+ * @return array
+ */
+ public function getLocks($uri, $returnChildLocks) {
+
+ // NOTE: the following 10 lines or so could be easily replaced by
+ // pure sql. MySQL's non-standard string concatination prevents us
+ // from doing this though.
+ // NOTE: SQLite requires time() to be inserted directly. That's ugly
+ // but otherwise reading locks from SQLite Databases will return
+ // nothing
+ $query = 'SELECT * FROM `*PREFIX*locks`'
+ .' WHERE `userid` = ? AND (`created` + `timeout`) > '.time().' AND (( `uri` = ?)';
+ if (OC_Config::getValue( "dbtype") === 'oci') {
+ //FIXME oracle hack: need to explicitly cast CLOB to CHAR for comparison
+ $query = 'SELECT * FROM `*PREFIX*locks`'
+ .' WHERE `userid` = ? AND (`created` + `timeout`) > '.time().' AND (( to_char(`uri`) = ?)';
+ }
+ $params = array(OC_User::getUser(), $uri);
+
+ // We need to check locks for every part in the uri.
+ $uriParts = explode('/', $uri);
+
+ // We already covered the last part of the uri
+ array_pop($uriParts);
+
+ $currentPath='';
+
+ foreach($uriParts as $part) {
+
+ if ($currentPath) $currentPath.='/';
+ $currentPath.=$part;
+ //FIXME oracle hack: need to explicitly cast CLOB to CHAR for comparison
+ if (OC_Config::getValue( "dbtype") === 'oci') {
+ $query.=' OR (`depth` != 0 AND to_char(`uri`) = ?)';
+ } else {
+ $query.=' OR (`depth` != 0 AND `uri` = ?)';
+ }
+ $params[] = $currentPath;
+
+ }
+
+ if ($returnChildLocks) {
+
+ //FIXME oracle hack: need to explicitly cast CLOB to CHAR for comparison
+ if (OC_Config::getValue( "dbtype") === 'oci') {
+ $query.=' OR (to_char(`uri`) LIKE ?)';
+ } else {
+ $query.=' OR (`uri` LIKE ?)';
+ }
+ $params[] = $uri . '/%';
+
+ }
+ $query.=')';
+
+ $result = OC_DB::executeAudited( $query, $params );
+
+ $lockList = array();
+ while( $row = $result->fetchRow()) {
+
+ $lockInfo = new Sabre_DAV_Locks_LockInfo();
+ $lockInfo->owner = $row['owner'];
+ $lockInfo->token = $row['token'];
+ $lockInfo->timeout = $row['timeout'];
+ $lockInfo->created = $row['created'];
+ $lockInfo->scope = $row['scope'];
+ $lockInfo->depth = $row['depth'];
+ $lockInfo->uri = $row['uri'];
+ $lockList[] = $lockInfo;
+
+ }
+
+ return $lockList;
+
+ }
+
+ /**
+ * Locks a uri
+ *
+ * @param string $uri
+ * @param Sabre_DAV_Locks_LockInfo $lockInfo
+ * @return bool
+ */
+ public function lock($uri, Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+ // We're making the lock timeout 5 minutes
+ $lockInfo->timeout = 300;
+ $lockInfo->created = time();
+ $lockInfo->uri = $uri;
+
+ $locks = $this->getLocks($uri, false);
+ $exists = false;
+ foreach($locks as $lock) {
+ if ($lock->token == $lockInfo->token) {
+ $exists = true;
+ break;
+ }
+ }
+
+ if ($exists) {
+ $sql = 'UPDATE `*PREFIX*locks`'
+ .' SET `owner` = ?, `timeout` = ?, `scope` = ?, `depth` = ?, `uri` = ?, `created` = ?'
+ .' WHERE `userid` = ? AND `token` = ?';
+ $result = OC_DB::executeAudited( $sql, array(
+ $lockInfo->owner,
+ $lockInfo->timeout,
+ $lockInfo->scope,
+ $lockInfo->depth,
+ $uri,
+ $lockInfo->created,
+ OC_User::getUser(),
+ $lockInfo->token)
+ );
+ } else {
+ $sql = 'INSERT INTO `*PREFIX*locks`'
+ .' (`userid`,`owner`,`timeout`,`scope`,`depth`,`uri`,`created`,`token`)'
+ .' VALUES (?,?,?,?,?,?,?,?)';
+ $result = OC_DB::executeAudited( $sql, array(
+ OC_User::getUser(),
+ $lockInfo->owner,
+ $lockInfo->timeout,
+ $lockInfo->scope,
+ $lockInfo->depth,
+ $uri,
+ $lockInfo->created,
+ $lockInfo->token)
+ );
+ }
+
+ return true;
+
+ }
+
+ /**
+ * Removes a lock from a uri
+ *
+ * @param string $uri
+ * @param Sabre_DAV_Locks_LockInfo $lockInfo
+ * @return bool
+ */
+ public function unlock($uri, Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+ $sql = 'DELETE FROM `*PREFIX*locks` WHERE `userid` = ? AND `uri` = ? AND `token` = ?';
+ if (OC_Config::getValue( "dbtype") === 'oci') {
+ //FIXME oracle hack: need to explicitly cast CLOB to CHAR for comparison
+ $sql = 'DELETE FROM `*PREFIX*locks` WHERE `userid` = ? AND to_char(`uri`) = ? AND `token` = ?';
+ }
+ $result = OC_DB::executeAudited( $sql, array(OC_User::getUser(), $uri, $lockInfo->token));
+
+ return $result === 1;
+
+ }
+
+}
diff --git a/lib/private/connector/sabre/maintenanceplugin.php b/lib/private/connector/sabre/maintenanceplugin.php
new file mode 100644
index 00000000000..2eda269afc2
--- /dev/null
+++ b/lib/private/connector/sabre/maintenanceplugin.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @author Thomas Müller
+ * @copyright 2013 Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @license AGPL3
+ */
+
+require 'ServiceUnavailable.php';
+
+class OC_Connector_Sabre_MaintenancePlugin extends Sabre_DAV_ServerPlugin
+{
+
+ /**
+ * Reference to main server object
+ *
+ * @var Sabre_DAV_Server
+ */
+ private $server;
+
+ /**
+ * This initializes the plugin.
+ *
+ * This function is called by Sabre_DAV_Server, after
+ * addPlugin is called.
+ *
+ * This method should set up the required event subscriptions.
+ *
+ * @param Sabre_DAV_Server $server
+ * @return void
+ */
+ public function initialize(Sabre_DAV_Server $server) {
+
+ $this->server = $server;
+ $this->server->subscribeEvent('beforeMethod', array($this, 'checkMaintenanceMode'), 10);
+ }
+
+ /**
+ * This method is called before any HTTP method and returns http status code 503
+ * in case the system is in maintenance mode.
+ *
+ * @throws Sabre_DAV_Exception_ServiceUnavailable
+ * @internal param string $method
+ * @return bool
+ */
+ public function checkMaintenanceMode() {
+ if (OC_Config::getValue('maintenance', false)) {
+ throw new Sabre_DAV_Exception_ServiceUnavailable();
+ }
+ if (OC::checkUpgrade(false)) {
+ throw new Sabre_DAV_Exception_ServiceUnavailable('Upgrade needed');
+ }
+
+ return true;
+ }
+}
diff --git a/lib/private/connector/sabre/node.php b/lib/private/connector/sabre/node.php
new file mode 100644
index 00000000000..fa27abb381a
--- /dev/null
+++ b/lib/private/connector/sabre/node.php
@@ -0,0 +1,258 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack kde@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+
+abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IProperties {
+ const GETETAG_PROPERTYNAME = '{DAV:}getetag';
+ const LASTMODIFIED_PROPERTYNAME = '{DAV:}lastmodified';
+
+ /**
+ * Allow configuring the method used to generate Etags
+ *
+ * @var array(class_name, function_name)
+ */
+ public static $ETagFunction = null;
+
+ /**
+ * is kept public to allow overwrite for unit testing
+ *
+ * @var \OC\Files\View
+ */
+ public $fileView;
+
+ /**
+ * The path to the current node
+ *
+ * @var string
+ */
+ protected $path;
+ /**
+ * node fileinfo cache
+ * @var array
+ */
+ protected $fileinfo_cache;
+ /**
+ * node properties cache
+ * @var array
+ */
+ protected $property_cache = null;
+
+ /**
+ * @brief Sets up the node, expects a full path name
+ * @param string $path
+ * @return void
+ */
+ public function __construct($path) {
+ $this->path = $path;
+ }
+
+
+
+ /**
+ * @brief Returns the name of the node
+ * @return string
+ */
+ public function getName() {
+
+ list(, $name) = Sabre_DAV_URLUtil::splitPath($this->path);
+ return $name;
+
+ }
+
+ /**
+ * @brief Renames the node
+ * @param string $name The new name
+ * @return void
+ */
+ public function setName($name) {
+
+ // rename is only allowed if the update privilege is granted
+ if (!\OC\Files\Filesystem::isUpdatable($this->path)) {
+ throw new \Sabre_DAV_Exception_Forbidden();
+ }
+
+ list($parentPath, ) = Sabre_DAV_URLUtil::splitPath($this->path);
+ list(, $newName) = Sabre_DAV_URLUtil::splitPath($name);
+
+ $newPath = $parentPath . '/' . $newName;
+ $oldPath = $this->path;
+
+ \OC\Files\Filesystem::rename($this->path, $newPath);
+
+ $this->path = $newPath;
+
+ $query = OC_DB::prepare( 'UPDATE `*PREFIX*properties` SET `propertypath` = ?'
+ .' WHERE `userid` = ? AND `propertypath` = ?' );
+ $query->execute( array( $newPath, OC_User::getUser(), $oldPath ));
+
+ }
+
+ public function setFileinfoCache($fileinfo_cache)
+ {
+ $this->fileinfo_cache = $fileinfo_cache;
+ }
+
+ /**
+ * @brief Ensure that the fileinfo cache is filled
+ * @note Uses OC_FileCache or a direct stat
+ */
+ protected function getFileinfoCache() {
+ if (!isset($this->fileinfo_cache)) {
+ if ($fileinfo_cache = \OC\Files\Filesystem::getFileInfo($this->path)) {
+ } else {
+ $fileinfo_cache = \OC\Files\Filesystem::stat($this->path);
+ }
+
+ $this->fileinfo_cache = $fileinfo_cache;
+ }
+ }
+
+ public function setPropertyCache($property_cache)
+ {
+ $this->property_cache = $property_cache;
+ }
+
+ /**
+ * @brief Returns the last modification time, as a unix timestamp
+ * @return int
+ */
+ public function getLastModified() {
+ $this->getFileinfoCache();
+ return $this->fileinfo_cache['mtime'];
+
+ }
+
+ /**
+ * sets the last modification time of the file (mtime) to the value given
+ * in the second parameter or to now if the second param is empty.
+ * Even if the modification time is set to a custom value the access time is set to now.
+ */
+ public function touch($mtime) {
+
+ // touch is only allowed if the update privilege is granted
+ if (!\OC\Files\Filesystem::isUpdatable($this->path)) {
+ throw new \Sabre_DAV_Exception_Forbidden();
+ }
+
+ \OC\Files\Filesystem::touch($this->path, $mtime);
+ }
+
+ /**
+ * @brief Updates properties on this node,
+ * @param array $mutations
+ * @see Sabre_DAV_IProperties::updateProperties
+ * @return bool|array
+ */
+ public function updateProperties($properties) {
+ $existing = $this->getProperties(array());
+ foreach($properties as $propertyName => $propertyValue) {
+ // If it was null, we need to delete the property
+ if (is_null($propertyValue)) {
+ if(array_key_exists( $propertyName, $existing )) {
+ $query = OC_DB::prepare( 'DELETE FROM `*PREFIX*properties`'
+ .' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?' );
+ $query->execute( array( OC_User::getUser(), $this->path, $propertyName ));
+ }
+ }
+ else {
+ if( strcmp( $propertyName, self::GETETAG_PROPERTYNAME) === 0 ) {
+ \OC\Files\Filesystem::putFileInfo($this->path, array('etag'=> $propertyValue));
+ } elseif( strcmp( $propertyName, self::LASTMODIFIED_PROPERTYNAME) === 0 ) {
+ $this->touch($propertyValue);
+ } else {
+ if(!array_key_exists( $propertyName, $existing )) {
+ $query = OC_DB::prepare( 'INSERT INTO `*PREFIX*properties`'
+ .' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)' );
+ $query->execute( array( OC_User::getUser(), $this->path, $propertyName,$propertyValue ));
+ } else {
+ $query = OC_DB::prepare( 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?'
+ .' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?' );
+ $query->execute( array( $propertyValue,OC_User::getUser(), $this->path, $propertyName ));
+ }
+ }
+ }
+
+ }
+ $this->setPropertyCache(null);
+ return true;
+ }
+
+ /**
+ * @brief Returns a list of properties for this nodes.;
+ * @param array $properties
+ * @return array
+ * @note The properties list is a list of propertynames the client
+ * requested, encoded as xmlnamespace#tagName, for example:
+ * http://www.example.org/namespace#author If the array is empty, all
+ * properties should be returned
+ */
+ public function getProperties($properties) {
+ if (is_null($this->property_cache)) {
+ $sql = 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?';
+ $result = OC_DB::executeAudited( $sql, array( OC_User::getUser(), $this->path ) );
+
+ $this->property_cache = array();
+ while( $row = $result->fetchRow()) {
+ $this->property_cache[$row['propertyname']] = $row['propertyvalue'];
+ }
+
+ // Don't call the static getETagPropertyForPath, its result is not cached
+ $this->getFileinfoCache();
+ if ($this->fileinfo_cache['etag']) {
+ $this->property_cache[self::GETETAG_PROPERTYNAME] = '"'.$this->fileinfo_cache['etag'].'"';
+ } else {
+ $this->property_cache[self::GETETAG_PROPERTYNAME] = null;
+ }
+ }
+
+ // if the array was empty, we need to return everything
+ if(count($properties) == 0) {
+ return $this->property_cache;
+ }
+
+ $props = array();
+ foreach($properties as $property) {
+ if (isset($this->property_cache[$property])) $props[$property] = $this->property_cache[$property];
+ }
+ return $props;
+ }
+
+ /**
+ * Returns the ETag surrounded by double-quotes for this path.
+ * @param string $path Path of the file
+ * @return string|null Returns null if the ETag can not effectively be determined
+ */
+ protected function getETagPropertyForPath($path) {
+ $data = $this->getFS()->getFileInfo($path);
+ if (isset($data['etag'])) {
+ return '"'.$data['etag'].'"';
+ }
+ return null;
+ }
+
+ protected function getFS() {
+ if (is_null($this->fileView)) {
+ $this->fileView = \OC\Files\Filesystem::getView();
+ }
+ return $this->fileView;
+ }
+}
diff --git a/lib/private/connector/sabre/objecttree.php b/lib/private/connector/sabre/objecttree.php
new file mode 100644
index 00000000000..80c3840b99d
--- /dev/null
+++ b/lib/private/connector/sabre/objecttree.php
@@ -0,0 +1,142 @@
+<?php
+/**
+ * Copyright (c) 2013 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 OC\Connector\Sabre;
+
+use OC\Files\Filesystem;
+
+class ObjectTree extends \Sabre_DAV_ObjectTree {
+
+ /**
+ * keep this public to allow mock injection during unit test
+ *
+ * @var \OC\Files\View
+ */
+ public $fileView;
+
+ /**
+ * Returns the INode object for the requested path
+ *
+ * @param string $path
+ * @throws \Sabre_DAV_Exception_NotFound
+ * @return \Sabre_DAV_INode
+ */
+ public function getNodeForPath($path) {
+
+ $path = trim($path, '/');
+ if (isset($this->cache[$path])) {
+ return $this->cache[$path];
+ }
+
+ // Is it the root node?
+ if (!strlen($path)) {
+ return $this->rootNode;
+ }
+
+ $info = $this->getFileView()->getFileInfo($path);
+
+ if (!$info) {
+ throw new \Sabre_DAV_Exception_NotFound('File with name ' . $path . ' could not be located');
+ }
+
+ if ($info['mimetype'] === 'httpd/unix-directory') {
+ $node = new \OC_Connector_Sabre_Directory($path);
+ } else {
+ $node = new \OC_Connector_Sabre_File($path);
+ }
+
+ $node->setFileinfoCache($info);
+
+ $this->cache[$path] = $node;
+ return $node;
+
+ }
+
+ /**
+ * Moves a file from one location to another
+ *
+ * @param string $sourcePath The path to the file which should be moved
+ * @param string $destinationPath The full destination path, so not just the destination parent node
+ * @throws \Sabre_DAV_Exception_Forbidden
+ * @return int
+ */
+ public function move($sourcePath, $destinationPath) {
+
+ $sourceNode = $this->getNodeForPath($sourcePath);
+ if ($sourceNode instanceof \Sabre_DAV_ICollection and $this->nodeExists($destinationPath)) {
+ throw new \Sabre_DAV_Exception_Forbidden('Could not copy directory ' . $sourceNode . ', target exists');
+ }
+ list($sourceDir,) = \Sabre_DAV_URLUtil::splitPath($sourcePath);
+ list($destinationDir,) = \Sabre_DAV_URLUtil::splitPath($destinationPath);
+
+ // check update privileges
+ $fs = $this->getFileView();
+ if (!$fs->isUpdatable($sourcePath)) {
+ throw new \Sabre_DAV_Exception_Forbidden();
+ }
+ if ($sourceDir !== $destinationDir) {
+ // for a full move we need update privileges on sourcePath and sourceDir as well as destinationDir
+ if (!$fs->isUpdatable($sourceDir)) {
+ throw new \Sabre_DAV_Exception_Forbidden();
+ }
+ if (!$fs->isUpdatable($destinationDir)) {
+ throw new \Sabre_DAV_Exception_Forbidden();
+ }
+ }
+
+ $renameOkay = $fs->rename($sourcePath, $destinationPath);
+ if (!$renameOkay) {
+ throw new \Sabre_DAV_Exception_Forbidden('');
+ }
+
+ $this->markDirty($sourceDir);
+ $this->markDirty($destinationDir);
+
+ }
+
+ /**
+ * Copies a file or directory.
+ *
+ * This method must work recursively and delete the destination
+ * if it exists
+ *
+ * @param string $source
+ * @param string $destination
+ * @return void
+ */
+ public function copy($source, $destination) {
+
+ if (Filesystem::is_file($source)) {
+ Filesystem::copy($source, $destination);
+ } else {
+ Filesystem::mkdir($destination);
+ $dh = Filesystem::opendir($source);
+ if(is_resource($dh)) {
+ while (($subnode = readdir($dh)) !== false) {
+
+ if ($subnode == '.' || $subnode == '..') continue;
+ $this->copy($source . '/' . $subnode, $destination . '/' . $subnode);
+
+ }
+ }
+ }
+
+ list($destinationDir,) = \Sabre_DAV_URLUtil::splitPath($destination);
+ $this->markDirty($destinationDir);
+ }
+
+ /**
+ * @return \OC\Files\View
+ */
+ public function getFileView() {
+ if (is_null($this->fileView)) {
+ $this->fileView = \OC\Files\Filesystem::getView();
+ }
+ return $this->fileView;
+ }
+}
diff --git a/lib/private/connector/sabre/principal.php b/lib/private/connector/sabre/principal.php
new file mode 100644
index 00000000000..59a96797c16
--- /dev/null
+++ b/lib/private/connector/sabre/principal.php
@@ -0,0 +1,128 @@
+<?php
+/**
+ * Copyright (c) 2011 Jakob Sack mail@jakobsack.de
+ * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+class OC_Connector_Sabre_Principal implements Sabre_DAVACL_IPrincipalBackend {
+ /**
+ * Returns a list of principals based on a prefix.
+ *
+ * This prefix will often contain something like 'principals'. You are only
+ * expected to return principals that are in this base path.
+ *
+ * You are expected to return at least a 'uri' for every user, you can
+ * return any additional properties if you wish so. Common properties are:
+ * {DAV:}displayname
+ *
+ * @param string $prefixPath
+ * @return array
+ */
+ public function getPrincipalsByPrefix( $prefixPath ) {
+ $principals = array();
+
+ if ($prefixPath == 'principals') {
+ foreach(OC_User::getUsers() as $user) {
+ $user_uri = 'principals/'.$user;
+ $principals[] = array(
+ 'uri' => $user_uri,
+ '{DAV:}displayname' => $user,
+ );
+ }
+ }
+
+ return $principals;
+ }
+
+ /**
+ * Returns a specific principal, specified by it's path.
+ * The returned structure should be the exact same as from
+ * getPrincipalsByPrefix.
+ *
+ * @param string $path
+ * @return array
+ */
+ public function getPrincipalByPath($path) {
+ list($prefix, $name) = explode('/', $path);
+
+ if ($prefix == 'principals' && OC_User::userExists($name)) {
+ return array(
+ 'uri' => 'principals/'.$name,
+ '{DAV:}displayname' => $name,
+ );
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the list of members for a group-principal
+ *
+ * @param string $principal
+ * @return array
+ */
+ public function getGroupMemberSet($principal) {
+ // TODO: for now the group principal has only one member, the user itself
+ $principal = $this->getPrincipalByPath($principal);
+ if (!$principal) {
+ throw new Sabre_DAV_Exception('Principal not found');
+ }
+
+ return array(
+ $principal['uri']
+ );
+ }
+
+ /**
+ * Returns the list of groups a principal is a member of
+ *
+ * @param string $principal
+ * @return array
+ */
+ public function getGroupMembership($principal) {
+ list($prefix, $name) = Sabre_DAV_URLUtil::splitPath($principal);
+
+ $group_membership = array();
+ if ($prefix == 'principals') {
+ $principal = $this->getPrincipalByPath($principal);
+ if (!$principal) {
+ throw new Sabre_DAV_Exception('Principal not found');
+ }
+
+ // TODO: for now the user principal has only its own groups
+ return array(
+ 'principals/'.$name.'/calendar-proxy-read',
+ 'principals/'.$name.'/calendar-proxy-write',
+ // The addressbook groups are not supported in Sabre,
+ // see http://groups.google.com/group/sabredav-discuss/browse_thread/thread/ef2fa9759d55f8c#msg_5720afc11602e753
+ //'principals/'.$name.'/addressbook-proxy-read',
+ //'principals/'.$name.'/addressbook-proxy-write',
+ );
+ }
+ return $group_membership;
+ }
+
+ /**
+ * Updates the list of group members for a group principal.
+ *
+ * The principals should be passed as a list of uri's.
+ *
+ * @param string $principal
+ * @param array $members
+ * @return void
+ */
+ public function setGroupMemberSet($principal, array $members) {
+ throw new Sabre_DAV_Exception('Setting members of the group is not supported yet');
+ }
+
+ function updatePrincipal($path, $mutations) {
+ return 0;
+ }
+
+ function searchPrincipals($prefixPath, array $searchProperties) {
+ return array();
+ }
+}
diff --git a/lib/private/connector/sabre/quotaplugin.php b/lib/private/connector/sabre/quotaplugin.php
new file mode 100644
index 00000000000..ea2cb81d1f7
--- /dev/null
+++ b/lib/private/connector/sabre/quotaplugin.php
@@ -0,0 +1,97 @@
+<?php
+
+/**
+ * This plugin check user quota and deny creating files when they exceeds the quota.
+ *
+ * @author Sergio Cambra
+ * @copyright Copyright (C) 2012 entreCables S.L. All rights reserved.
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class OC_Connector_Sabre_QuotaPlugin extends Sabre_DAV_ServerPlugin {
+
+ /**
+ * Reference to main server object
+ *
+ * @var Sabre_DAV_Server
+ */
+ private $server;
+
+ /**
+ * is kept public to allow overwrite for unit testing
+ *
+ * @var \OC\Files\View
+ */
+ public $fileView;
+
+ /**
+ * This initializes the plugin.
+ *
+ * This function is called by Sabre_DAV_Server, after
+ * addPlugin is called.
+ *
+ * This method should set up the requires event subscriptions.
+ *
+ * @param Sabre_DAV_Server $server
+ * @return void
+ */
+ public function initialize(Sabre_DAV_Server $server) {
+
+ $this->server = $server;
+
+ $server->subscribeEvent('beforeWriteContent', array($this, 'checkQuota'), 10);
+ $server->subscribeEvent('beforeCreateFile', array($this, 'checkQuota'), 10);
+ }
+
+ /**
+ * This method is called before any HTTP method and validates there is enough free space to store the file
+ *
+ * @param string $method
+ * @throws Sabre_DAV_Exception
+ * @return bool
+ */
+ public function checkQuota($uri, $data = null) {
+ $length = $this->getLength();
+ if ($length) {
+ if (substr($uri, 0, 1)!=='/') {
+ $uri='/'.$uri;
+ }
+ list($parentUri, $newName) = Sabre_DAV_URLUtil::splitPath($uri);
+ $freeSpace = $this->getFreeSpace($parentUri);
+ if ($freeSpace !== \OC\Files\SPACE_UNKNOWN && $length > $freeSpace) {
+ throw new Sabre_DAV_Exception_InsufficientStorage();
+ }
+ }
+ return true;
+ }
+
+ public function getLength()
+ {
+ $req = $this->server->httpRequest;
+ $length = $req->getHeader('X-Expected-Entity-Length');
+ if (!$length) {
+ $length = $req->getHeader('Content-Length');
+ }
+
+ $ocLength = $req->getHeader('OC-Total-Length');
+ if ($length && $ocLength) {
+ return max($length, $ocLength);
+ }
+
+ return $length;
+ }
+
+ /**
+ * @param $parentUri
+ * @return mixed
+ */
+ public function getFreeSpace($parentUri)
+ {
+ if (is_null($this->fileView)) {
+ // initialize fileView
+ $this->fileView = \OC\Files\Filesystem::getView();
+ }
+
+ $freeSpace = $this->fileView->free_space($parentUri);
+ return $freeSpace;
+ }
+}
diff --git a/lib/private/connector/sabre/request.php b/lib/private/connector/sabre/request.php
new file mode 100644
index 00000000000..d70c25c4e70
--- /dev/null
+++ b/lib/private/connector/sabre/request.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @author Stefan Herbrechtsmeier
+ * @copyright 2012 Stefan Herbrechtsmeier <stefan@herbrechtsmeier.net>
+ *
+ * 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/>.
+ *
+ */
+
+class OC_Connector_Sabre_Request extends Sabre_HTTP_Request {
+ /**
+ * Returns the requested uri
+ *
+ * @return string
+ */
+ public function getUri() {
+ return OC_Request::requestUri();
+ }
+
+ /**
+ * Returns a specific item from the _SERVER array.
+ *
+ * Do not rely on this feature, it is for internal use only.
+ *
+ * @param string $field
+ * @return string
+ */
+ public function getRawServerValue($field) {
+ if($field == 'REQUEST_URI') {
+ return $this->getUri();
+ }
+ else{
+ return isset($this->_SERVER[$field])?$this->_SERVER[$field]:null;
+ }
+ }
+}