Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stable10] Fix quota federated sharing (new) #29325

Merged
merged 6 commits into from
Oct 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/dav/lib/Connector/Sabre/QuotaPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ public function checkQuota($path, $length = null) {
$path = rtrim($parentPath, '/') . '/' . $info['name'];
}
$freeSpace = $this->getFreeSpace($path);
if ($freeSpace !== FileInfo::SPACE_UNKNOWN && $length > $freeSpace) {
if ($freeSpace !== FileInfo::SPACE_UNKNOWN && $freeSpace !== FileInfo::SPACE_UNLIMITED && $length > $freeSpace) {
if (isset($chunkHandler)) {
$chunkHandler->cleanup();
}
Expand Down
12 changes: 12 additions & 0 deletions apps/dav/tests/unit/Connector/Sabre/QuotaPluginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ public function quotaOkayProvider() {
[-2, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
[-2, ['CONTENT-LENGTH' => '512']],
[-2, ['OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512']],
// \OCP\Files\FileInfo::SPACE-UNLIMITED = -3
[-3, []],
[-3, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
[-3, ['CONTENT-LENGTH' => '512']],
[-3, ['OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512']],
];
}

Expand Down Expand Up @@ -159,6 +164,13 @@ public function quotaChunkedOkProvider() {
[-2, 128, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
[-2, 128, ['CONTENT-LENGTH' => '512']],
[-2, 128, ['OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512']],
// \OCP\Files\FileInfo::SPACE-UNLIMITED = -3
[-3, 0, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
[-3, 0, ['CONTENT-LENGTH' => '512']],
[-3, 0, ['OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512']],
[-3, 128, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
[-3, 128, ['CONTENT-LENGTH' => '512']],
[-3, 128, ['OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512']],
];
}

Expand Down
113 changes: 64 additions & 49 deletions lib/private/Files/Storage/DAV.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@
use OCP\Files\StorageInvalidException;
use OCP\Files\StorageNotAvailableException;
use OCP\Util;
use Sabre\DAV\Client;
use Sabre\DAV\Xml\Property\ResourceType;
use Sabre\HTTP\ClientException;
use Sabre\HTTP\ClientHttpException;
use Sabre\DAV\Exception\InsufficientStorage;
use OCA\DAV\Connector\Sabre\Exception\Forbidden;

/**
* Class DAV
Expand Down Expand Up @@ -82,13 +83,17 @@ class DAV extends Common {
/** @var \OCP\Http\Client\IClientService */
private $httpClientService;

/** @var \OCP\Http\Client\IWebDavClientService */
private $webDavClientService;

/**
* @param array $params
* @throws \Exception
*/
public function __construct($params) {
$this->statCache = new ArrayCache();
$this->httpClientService = \OC::$server->getHTTPClientService();
$this->webDavClientService = \OC::$server->getWebDavClientService();
if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
$host = $params['host'];
//remove leading http[s], will be generated in createBaseUri()
Expand All @@ -109,17 +114,6 @@ public function __construct($params) {
} else {
$this->secure = false;
}
if ($this->secure === true) {
// inject mock for testing
$certManager = \OC::$server->getCertificateManager();
if (is_null($certManager)) { //no user
$certManager = \OC::$server->getCertificateManager(null);
}
$certPath = $certManager->getAbsoluteBundlePath();
if (file_exists($certPath)) {
$this->certPath = $certPath;
}
}
$this->root = isset($params['root']) ? $params['root'] : '/';
if (!$this->root || $this->root[0] != '/') {
$this->root = '/' . $this->root;
Expand All @@ -128,7 +122,7 @@ public function __construct($params) {
$this->root .= '/';
}
} else {
throw new \Exception('Invalid webdav storage configuration');
throw new \InvalidArgumentException('Invalid webdav storage configuration');
}
}

Expand All @@ -141,22 +135,13 @@ protected function init() {
$settings = [
'baseUri' => $this->createBaseUri(),
'userName' => $this->user,
'password' => $this->password,
'password' => $this->password
];
if (isset($this->authType)) {
$settings['authType'] = $this->authType;
}

$proxy = \OC::$server->getConfig()->getSystemValue('proxy', '');
if($proxy !== '') {
$settings['proxy'] = $proxy;
}

$this->client = new Client($settings);
$this->client->setThrowExceptions(true);
if ($this->secure === true && $this->certPath) {
$this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
}
$this->client = $this->webDavClientService->newClient($settings);
}

/**
Expand Down Expand Up @@ -234,6 +219,13 @@ public function opendir($path) {
$content[] = $file;
}
return IteratorDirectory::wrap($content);
} catch (ClientHttpException $e) {
if ($e->getHttpStatus() === Http::STATUS_NOT_FOUND) {
$this->statCache->clear($path . '/');
$this->statCache->set($path, false);
return false;
}
$this->convertException($e, $path);
} catch (\Exception $e) {
$this->convertException($e, $path);
}
Expand Down Expand Up @@ -355,7 +347,7 @@ public function fopen($path, $mode) {
&& $e->getResponse()->getStatusCode() === Http::STATUS_NOT_FOUND) {
return false;
} else {
throw $e;
$this->convertException($e);
}
}

Expand All @@ -364,6 +356,7 @@ public function fopen($path, $mode) {
throw new \OCP\Lock\LockedException($path);
} else {
Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), Util::ERROR);
// FIXME: why not returning false here ?!
}
}

Expand Down Expand Up @@ -442,7 +435,7 @@ public function free_space($path) {
public function touch($path, $mtime = null) {
$this->init();
if (is_null($mtime)) {
$mtime = time();
$mtime = \OC::$server->getTimeFactory()->getTime();
}
$path = $this->cleanPath($path);

Expand All @@ -454,6 +447,7 @@ public function touch($path, $mtime = null) {
// non-owncloud clients might not have accepted the property, need to recheck it
$response = $this->client->propfind($this->encodePath($path), ['{DAV:}getlastmodified'], 0);
if ($response === false) {
// file disappeared since ?
return false;
}
if (isset($response['{DAV:}getlastmodified'])) {
Expand Down Expand Up @@ -503,14 +497,17 @@ protected function uploadFile($path, $target) {
$this->statCache->remove($target);
$source = fopen($path, 'r');

$this->httpClientService
->newClient()
->put($this->createBaseUri() . $this->encodePath($target), [
'body' => $source,
'auth' => [$this->user, $this->password]
]);

$this->removeCachedFile($target);
try {
$this->httpClientService
->newClient()
->put($this->createBaseUri() . $this->encodePath($target), [
'body' => $source,
'auth' => [$this->user, $this->password]
]);
} catch (\Exception $e) {
$this->convertException($e);
}
}

/** {@inheritdoc} */
Expand Down Expand Up @@ -661,6 +658,9 @@ private function simpleResponse($method, $path, $body, $expected) {
$this->statCache->set($path, false);
return false;
}
if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED && $method === 'MKCOL') {
return false;
}

$this->convertException($e, $path);
} catch (\Exception $e) {
Expand Down Expand Up @@ -775,10 +775,7 @@ public function hasUpdated($path, $time) {
}
if (isset($response['{DAV:}getetag'])) {
$cachedData = $this->getCache()->get($path);
$etag = null;
if (isset($response['{DAV:}getetag'])) {
$etag = trim($response['{DAV:}getetag'], '"');
}
$etag = trim($response['{DAV:}getetag'], '"');
if (!empty($etag) && $cachedData['etag'] !== $etag) {
return true;
} else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
Expand Down Expand Up @@ -828,30 +825,48 @@ private function convertException(Exception $e, $path = '') {
\OC::$server->getLogger()->logException($e);
Util::writeLog('files_external', $e->getMessage(), Util::ERROR);
if ($e instanceof ClientHttpException) {
if ($e->getHttpStatus() === Http::STATUS_LOCKED) {
throw new \OCP\Lock\LockedException($path);
$this->throwByStatusCode($e->getHttpStatus(), $e, $path);
} else if ($e instanceof \GuzzleHttp\Exception\RequestException) {
if ($e->getResponse() instanceof ResponseInterface) {
$this->throwByStatusCode($e->getResponse()->getStatusCode(), $e);
}
if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
// either password was changed or was invalid all along
throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
} else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
// ignore exception for MethodNotAllowed, false will be returned
return;
}
throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
} else if ($e instanceof ClientException) {
// connection timeout or refused, server could be temporarily down
throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
} else if ($e instanceof \InvalidArgumentException) {
// parse error because the server returned HTML instead of XML,
// possibly temporarily down
throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
} else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
} else if (($e instanceof StorageNotAvailableException)
|| ($e instanceof StorageInvalidException)
|| ($e instanceof \Sabre\DAV\Exception
)) {
// rethrow
throw $e;
}

// TODO: only log for now, but in the future need to wrap/rethrow exception
}

/**
* Throw exception by status code
*
* @param int $statusCode status code
* @param string $path optional path for some exceptions
* @throws \Exception Sabre or ownCloud exceptions
*/
private function throwByStatusCode($statusCode, $e, $path = '') {
switch ($statusCode) {
case Http::STATUS_LOCKED:
throw new \OCP\Lock\LockedException($path);
case Http::STATUS_UNAUTHORIZED:
// either password was changed or was invalid all along
throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
case Http::STATUS_INSUFFICIENT_STORAGE:
throw new InsufficientStorage();
case Http::STATUS_FORBIDDEN:
throw new Forbidden('Forbidden');
}
throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
}
}

97 changes: 97 additions & 0 deletions lib/private/Http/Client/WebDavClientService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php
/**
* @author Vincent Petry <[email protected]>
*
* @copyright Copyright (c) 2017, ownCloud GmbH
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/

namespace OC\Http\Client;

use OCP\Http\Client\IWebDavClientService;
use OCP\IConfig;
use OCP\ICertificateManager;
use Sabre\DAV\Client;

/**
* Class WebDavClientService
*
* @package OC\Http
*/
class WebDavClientService implements IWebDavClientService {
/** @var IConfig */
private $config;
/** @var ICertificateManager */
private $certificateManager;

/**
* @param IConfig $config
* @param ICertificateManager $certificateManager
*/
public function __construct(IConfig $config,
ICertificateManager $certificateManager) {
$this->config = $config;
$this->certificateManager = $certificateManager;
}

/**
* Instantiate new Sabre client
*
* Settings are provided through the 'settings' argument. The following
* settings are supported:
*
* * baseUri
* * userName (optional)
* * password (optional)
* * proxy (optional)
* * authType (optional)
* * encoding (optional)
*
* authType must be a bitmap, using self::AUTH_BASIC, self::AUTH_DIGEST
* and self::AUTH_NTLM. If you know which authentication method will be
* used, it's recommended to set it, as it will save a great deal of
* requests to 'discover' this information.
*
* Encoding is a bitmap with one of the ENCODING constants.
*
* @param array $settings Sabre client settings
* @return Client
*/
public function newClient($settings) {
if (!isset($settings['proxy'])) {
$proxy = $this->config->getSystemValue('proxy', '');
if($proxy !== '') {
$settings['proxy'] = $proxy;
}
}

$certPath = null;
if (strpos($settings['baseUri'], 'https') === 0) {
$certPath = $this->certificateManager->getAbsoluteBundlePath();
if (!file_exists($certPath)) {
$certPath = null;
}
}

$client = new Client($settings);
$client->setThrowExceptions(true);

if ($certPath !== null) {
$client->addCurlSetting(CURLOPT_CAINFO, $certPath);
}
return $client;
}
}
Loading