Merge pull request #9311 from owncloud/storage-not-available

Handle storages not being available in webui and webdav
This commit is contained in:
Vincent Petry 2014-07-02 18:15:58 +02:00
commit f4eb90e229
12 changed files with 232 additions and 70 deletions

View File

@ -2,29 +2,54 @@
OCP\JSON::checkLoggedIn(); OCP\JSON::checkLoggedIn();
\OC::$session->close(); \OC::$session->close();
$l = OC_L10N::get('files');
// Load the files // Load the files
$dir = isset( $_GET['dir'] ) ? $_GET['dir'] : ''; $dir = isset($_GET['dir']) ? $_GET['dir'] : '';
$dir = \OC\Files\Filesystem::normalizePath($dir); $dir = \OC\Files\Filesystem::normalizePath($dir);
$dirInfo = \OC\Files\Filesystem::getFileInfo($dir);
if (!$dirInfo || !$dirInfo->getType() === 'dir') { try {
header("HTTP/1.0 404 Not Found"); $dirInfo = \OC\Files\Filesystem::getFileInfo($dir);
exit(); if (!$dirInfo || !$dirInfo->getType() === 'dir') {
header("HTTP/1.0 404 Not Found");
exit();
}
$data = array();
$baseUrl = OCP\Util::linkTo('files', 'index.php') . '?dir=';
$permissions = $dirInfo->getPermissions();
$sortAttribute = isset($_GET['sort']) ? $_GET['sort'] : 'name';
$sortDirection = isset($_GET['sortdirection']) ? ($_GET['sortdirection'] === 'desc') : false;
// make filelist
$files = \OCA\Files\Helper::getFiles($dir, $sortAttribute, $sortDirection);
$data['directory'] = $dir;
$data['files'] = \OCA\Files\Helper::formatFileInfos($files);
$data['permissions'] = $permissions;
OCP\JSON::success(array('data' => $data));
} catch (\OCP\Files\StorageNotAvailableException $e) {
OCP\JSON::error(array(
'data' => array(
'exception' => '\OCP\Files\StorageNotAvailableException',
'message' => $l->t('Storage not available')
)
));
} catch (\OCP\Files\StorageInvalidException $e) {
OCP\JSON::error(array(
'data' => array(
'exception' => '\OCP\Files\StorageInvalidException',
'message' => $l->t('Storage invalid')
)
));
} catch (\Exception $e) {
OCP\JSON::error(array(
'data' => array(
'exception' => '\Exception',
'message' => $l->t('Unknown error')
)
));
} }
$data = array();
$baseUrl = OCP\Util::linkTo('files', 'index.php') . '?dir=';
$permissions = $dirInfo->getPermissions();
$sortAttribute = isset( $_GET['sort'] ) ? $_GET['sort'] : 'name';
$sortDirection = isset( $_GET['sortdirection'] ) ? ($_GET['sortdirection'] === 'desc') : false;
// make filelist
$files = \OCA\Files\Helper::getFiles($dir, $sortAttribute, $sortDirection);
$data['directory'] = $dir;
$data['files'] = \OCA\Files\Helper::formatFileInfos($files);
$data['permissions'] = $permissions;
OCP\JSON::success(array('data' => $data));

View File

@ -846,13 +846,18 @@
* @param {boolean} force set to true to force changing directory * @param {boolean} force set to true to force changing directory
*/ */
changeDirectory: function(targetDir, changeUrl, force) { changeDirectory: function(targetDir, changeUrl, force) {
var self = this;
var currentDir = this.getCurrentDirectory(); var currentDir = this.getCurrentDirectory();
targetDir = targetDir || '/'; targetDir = targetDir || '/';
if (!force && currentDir === targetDir) { if (!force && currentDir === targetDir) {
return; return;
} }
this._setCurrentDir(targetDir, changeUrl); this._setCurrentDir(targetDir, changeUrl);
this.reload(); this.reload().then(function(success){
if (!success) {
self.changeDirectory(currentDir, true);
}
});
}, },
linkTo: function(dir) { linkTo: function(dir) {
return OC.linkTo('files', 'index.php')+"?dir="+ encodeURIComponent(dir).replace(/%2F/g, '/'); return OC.linkTo('files', 'index.php')+"?dir="+ encodeURIComponent(dir).replace(/%2F/g, '/');
@ -912,7 +917,6 @@
* @brief Reloads the file list using ajax call * @brief Reloads the file list using ajax call
*/ */
reload: function() { reload: function() {
var self = this;
this._selectedFiles = {}; this._selectedFiles = {};
this._selectionSummary.clear(); this._selectionSummary.clear();
this.$el.find('.select-all').prop('checked', false); this.$el.find('.select-all').prop('checked', false);
@ -926,14 +930,10 @@
dir : this.getCurrentDirectory(), dir : this.getCurrentDirectory(),
sort: this._sort, sort: this._sort,
sortdirection: this._sortDirection sortdirection: this._sortDirection
},
error: function(result) {
self.reloadCallback(result);
},
success: function(result) {
self.reloadCallback(result);
} }
}); });
var callBack = this.reloadCallback.bind(this);
return this._reloadCall.then(callBack, callBack);
}, },
reloadCallback: function(result) { reloadCallback: function(result) {
delete this._reloadCall; delete this._reloadCall;
@ -941,17 +941,17 @@
if (!result || result.status === 'error') { if (!result || result.status === 'error') {
OC.Notification.show(result.data.message); OC.Notification.show(result.data.message);
return; return false;
} }
if (result.status === 404) { if (result.status === 404) {
// go back home // go back home
this.changeDirectory('/'); this.changeDirectory('/');
return; return false;
} }
// aborted ? // aborted ?
if (result.status === 0){ if (result.status === 0){
return; return true;
} }
// TODO: should rather return upload file size through // TODO: should rather return upload file size through
@ -963,6 +963,7 @@
} }
this.setFiles(result.data.files); this.setFiles(result.data.files);
return true
}, },
updateStorageStatistics: function(force) { updateStorageStatistics: function(force) {

View File

@ -58,7 +58,7 @@ $(document).ready(function () {
if (params.remote && params.token && params.owner && params.name) { if (params.remote && params.token && params.owner && params.name) {
// clear hash, it is unlikely that it contain any extra parameters // clear hash, it is unlikely that it contain any extra parameters
location.hash = ''; location.hash = '';
params.passwordProtected = parseInt(params.passwordProtected, 10) === 1; params.passwordProtected = parseInt(params.protected, 10) === 1;
OCA.Sharing.showAddExternalDialog( OCA.Sharing.showAddExternalDialog(
params.remote, params.remote,
params.token, params.token,

View File

@ -113,7 +113,9 @@ class Manager {
* @return Mount * @return Mount
*/ */
protected function mountShare($data) { protected function mountShare($data) {
$data['manager'] = $this;
$mountPoint = '/' . $this->userSession->getUser()->getUID() . '/files' . $data['mountpoint']; $mountPoint = '/' . $this->userSession->getUser()->getUID() . '/files' . $data['mountpoint'];
$data['mountpoint'] = $mountPoint;
$mount = new Mount(self::STORAGE, $mountPoint, $data, $this, $this->storageLoader); $mount = new Mount(self::STORAGE, $mountPoint, $data, $this, $this->storageLoader);
$this->mountManager->addMount($mount); $this->mountManager->addMount($mount);
return $mount; return $mount;

View File

@ -19,23 +19,7 @@ class Scanner extends \OC\Files\Cache\Scanner {
} }
public function scanAll() { public function scanAll() {
$remote = $this->storage->getRemote(); $data = $this->storage->getShareInfo();
$token = $this->storage->getToken();
$password = $this->storage->getPassword();
$url = $remote . '/index.php/apps/files_sharing/shareinfo?t=' . $token;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,
http_build_query(array('password' => $password)));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
$data = json_decode($result, true);
if ($data['status'] === 'success') { if ($data['status'] === 'success') {
$this->addResult($data['data'], ''); $this->addResult($data['data'], '');
} else { } else {

View File

@ -10,7 +10,11 @@ namespace OCA\Files_Sharing\External;
use OC\Files\Filesystem; use OC\Files\Filesystem;
use OC\Files\Storage\DAV; use OC\Files\Storage\DAV;
use OC\ForbiddenException;
use OCA\Files_Sharing\ISharedStorage; use OCA\Files_Sharing\ISharedStorage;
use OCP\Files\NotFoundException;
use OCP\Files\StorageInvalidException;
use OCP\Files\StorageNotAvailableException;
class Storage extends DAV implements ISharedStorage { class Storage extends DAV implements ISharedStorage {
/** /**
@ -35,7 +39,13 @@ class Storage extends DAV implements ISharedStorage {
private $updateChecked = false; private $updateChecked = false;
/**
* @var \OCA\Files_Sharing\External\Manager
*/
private $manager;
public function __construct($options) { public function __construct($options) {
$this->manager = $options['manager'];
$this->remote = $options['remote']; $this->remote = $options['remote'];
$this->remoteUser = $options['owner']; $this->remoteUser = $options['owner'];
list($protocol, $remote) = explode('://', $this->remote); list($protocol, $remote) = explode('://', $this->remote);
@ -108,6 +118,8 @@ class Storage extends DAV implements ISharedStorage {
* *
* @param string $path * @param string $path
* @param int $time * @param int $time
* @throws \OCP\Files\StorageNotAvailableException
* @throws \OCP\Files\StorageInvalidException
* @return bool * @return bool
*/ */
public function hasUpdated($path, $time) { public function hasUpdated($path, $time) {
@ -117,6 +129,77 @@ class Storage extends DAV implements ISharedStorage {
return false; return false;
} }
$this->updateChecked = true; $this->updateChecked = true;
return parent::hasUpdated('', $time); try {
return parent::hasUpdated('', $time);
} catch (StorageNotAvailableException $e) {
// see if we can find out why the share is unavailable\
try {
$this->getShareInfo();
} catch (NotFoundException $shareException) {
// a 404 can either mean that the share no longer exists or there is no ownCloud on the remote
if ($this->testRemote()) {
// valid ownCloud instance means that the public share no longer exists
// since this is permanent (re-sharing the file will create a new token)
// we remove the invalid storage
$this->manager->removeShare($this->mountPoint);
$this->manager->getMountManager()->removeMount($this->mountPoint);
throw new StorageInvalidException();
} else {
// ownCloud instance is gone, likely to be a temporary server configuration error
throw $e;
}
} catch(\Exception $shareException) {
// todo, maybe handle 403 better and ask the user for a new password
throw $e;
}
throw $e;
}
}
/**
* check if the configured remote is a valid ownCloud instance
*
* @return bool
*/
protected function testRemote() {
try {
$result = file_get_contents($this->remote . '/status.php');
$data = json_decode($result);
return is_object($data) and !empty($data->version);
} catch (\Exception $e) {
return false;
}
}
public function getShareInfo() {
$remote = $this->getRemote();
$token = $this->getToken();
$password = $this->getPassword();
$url = $remote . '/index.php/apps/files_sharing/shareinfo?t=' . $token;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,
http_build_query(array('password' => $password)));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
switch ($status) {
case 401:
case 403:
throw new ForbiddenException();
case 404:
throw new NotFoundException();
case 500:
throw new \Exception();
}
return json_decode($result, true);
} }
} }

View File

@ -72,6 +72,7 @@ class Test_Files_Sharing_External_Storage extends \PHPUnit_Framework_TestCase {
'mountpoint' => 'remoteshare', 'mountpoint' => 'remoteshare',
'token' => 'abcdef', 'token' => 'abcdef',
'password' => '', 'password' => '',
'manager' => null
) )
); );
$this->assertEquals($baseUri, $storage->getBaseUri()); $this->assertEquals($baseUri, $storage->getBaseUri());

View File

@ -11,6 +11,8 @@ namespace OC\Connector\Sabre;
use OC\Files\FileInfo; use OC\Files\FileInfo;
use OC\Files\Filesystem; use OC\Files\Filesystem;
use OC\Files\Mount\MoveableMount; use OC\Files\Mount\MoveableMount;
use OCP\Files\StorageInvalidException;
use OCP\Files\StorageNotAvailableException;
class ObjectTree extends \Sabre\DAV\ObjectTree { class ObjectTree extends \Sabre\DAV\ObjectTree {
@ -83,7 +85,13 @@ class ObjectTree extends \Sabre\DAV\ObjectTree {
} }
} else { } else {
// read from cache // read from cache
$info = $this->fileView->getFileInfo($path); try {
$info = $this->fileView->getFileInfo($path);
} catch (StorageNotAvailableException $e) {
throw new \Sabre\DAV\Exception\ServiceUnavailable('Storage not available');
} catch (StorageInvalidException $e){
throw new \Sabre\DAV\Exception\NotFound('Storage ' . $path . ' is invalid');
}
} }
if (!$info) { if (!$info) {

View File

@ -27,6 +27,10 @@ class Manager {
* @param string $mountPoint * @param string $mountPoint
*/ */
public function removeMount($mountPoint) { public function removeMount($mountPoint) {
$mountPoint = Filesystem::normalizePath($mountPoint);
if (strlen($mountPoint) > 1) {
$mountPoint .= '/';
}
unset($this->mounts[$mountPoint]); unset($this->mounts[$mountPoint]);
} }

View File

@ -8,6 +8,9 @@
namespace OC\Files\Storage; namespace OC\Files\Storage;
use OCP\Files\StorageNotAvailableException;
use Sabre\DAV\Exception;
class DAV extends \OC\Files\Storage\Common { class DAV extends \OC\Files\Storage\Common {
protected $password; protected $password;
protected $user; protected $user;
@ -463,29 +466,36 @@ class DAV extends \OC\Files\Storage\Common {
* *
* @param string $path * @param string $path
* @param int $time * @param int $time
* @throws \OCP\Files\StorageNotAvailableException
* @return bool * @return bool
*/ */
public function hasUpdated($path, $time) { public function hasUpdated($path, $time) {
$this->init(); $this->init();
$response = $this->client->propfind($this->encodePath($path), array( try {
'{DAV:}getlastmodified', $response = $this->client->propfind($this->encodePath($path), array(
'{DAV:}getetag', '{DAV:}getlastmodified',
'{http://owncloud.org/ns}permissions' '{DAV:}getetag',
)); '{http://owncloud.org/ns}permissions'
if (isset($response['{DAV:}getetag'])) { ));
$cachedData = $this->getCache()->get($path); if (isset($response['{DAV:}getetag'])) {
$etag = trim($response['{DAV:}getetag'], '"'); $cachedData = $this->getCache()->get($path);
if ($cachedData['etag'] !== $etag) { $etag = trim($response['{DAV:}getetag'], '"');
return true; if ($cachedData['etag'] !== $etag) {
} else if (isset($response['{http://owncloud.org/ns}permissions'])) { return true;
$permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']); } else if (isset($response['{http://owncloud.org/ns}permissions'])) {
return $permissions !== $cachedData['permissions']; $permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
return $permissions !== $cachedData['permissions'];
} else {
return false;
}
} else { } else {
return false; $remoteMtime = strtotime($response['{DAV:}getlastmodified']);
return $remoteMtime > $time;
} }
} else { } catch (Exception\NotFound $e) {
$remoteMtime = strtotime($response['{DAV:}getlastmodified']); return false;
return $remoteMtime > $time; } catch (Exception $e) {
throw new StorageNotAvailableException();
} }
} }
} }

View File

@ -0,0 +1,22 @@
<?php
/**
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
/**
* Public interface of ownCloud for apps to use.
* Files/AlreadyExistsException class
*/
// use OCP namespace for all classes that are considered public.
// This means that they should be used by apps instead of the internal ownCloud classes
namespace OCP\Files;
/**
* Storage has invalid configuration
*/
class StorageInvalidException extends \Exception {
}

View File

@ -0,0 +1,22 @@
<?php
/**
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
/**
* Public interface of ownCloud for apps to use.
* Files/AlreadyExistsException class
*/
// use OCP namespace for all classes that are considered public.
// This means that they should be used by apps instead of the internal ownCloud classes
namespace OCP\Files;
/**
* Storage is temporarily not available
*/
class StorageNotAvailableException extends \Exception {
}