diff --git a/apps/files/appinfo/remote.php b/apps/files/appinfo/remote.php index 92c7618387..dd5c470431 100644 --- a/apps/files/appinfo/remote.php +++ b/apps/files/appinfo/remote.php @@ -49,8 +49,9 @@ $server->subscribeEvent('beforeMethod', function () use ($server, $objectTree) { $rootInfo = $view->getFileInfo(''); // Create ownCloud Dir + $mountManager = \OC\Files\Filesystem::getMountManager(); $rootDir = new OC_Connector_Sabre_Directory($view, $rootInfo); - $objectTree->init($rootDir, $view); + $objectTree->init($rootDir, $view, $mountManager); $server->addPlugin(new OC_Connector_Sabre_AbortedUploadDetectionPlugin($view)); $server->addPlugin(new OC_Connector_Sabre_QuotaPlugin($view)); diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index fd91073b8d..b1af467685 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -41,6 +41,39 @@ class Proxy extends \OC_FileProxy { private static $fopenMode = array(); // remember the fopen mode private static $enableEncryption = false; // Enable encryption for the given path + + /** + * check if path is excluded from encryption + * + * @param string $path relative to data/ + * @param string $uid user + * @return boolean + */ + private function isExcludedPath($path, $uid) { + + $view = new \OC\Files\View(); + + // files outside of the files-folder are excluded + if(strpos($path, '/' . $uid . '/files') !== 0) { + return true; + } + + if (!$view->file_exists($path)) { + $path = dirname($path); + } + + // we don't encrypt server-to-server shares + list($storage, ) = \OC\Files\Filesystem::resolvePath($path); + /** + * @var \OCP\Files\Storage $storage + */ + if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) { + return true; + } + + return false; + } + /** * Check if a file requires encryption * @param string $path @@ -50,7 +83,7 @@ class Proxy extends \OC_FileProxy { * Tests if server side encryption is enabled, and if we should call the * crypt stream wrapper for the given file */ - private static function shouldEncrypt($path, $mode = 'w') { + private function shouldEncrypt($path, $mode = 'w') { $userId = Helper::getUser($path); $session = new Session(new \OC\Files\View()); @@ -59,7 +92,7 @@ class Proxy extends \OC_FileProxy { if ( $session->getInitialized() !== Session::INIT_SUCCESSFUL // encryption successful initialized || Crypt::mode() !== 'server' // we are not in server-side-encryption mode - || strpos($path, '/' . $userId . '/files') !== 0 // path is not in files/ + || $this->isExcludedPath($path, $userId) // if path is excluded from encryption || substr($path, 0, 8) === 'crypt://' // we are already in crypt mode ) { return false; @@ -85,7 +118,7 @@ class Proxy extends \OC_FileProxy { */ public function preFile_put_contents($path, &$data) { - if (self::shouldEncrypt($path)) { + if ($this->shouldEncrypt($path)) { if (!is_resource($data)) { @@ -219,7 +252,7 @@ class Proxy extends \OC_FileProxy { public function preFopen($path, $mode) { self::$fopenMode[$path] = $mode; - self::$enableEncryption = self::shouldEncrypt($path, $mode); + self::$enableEncryption = $this->shouldEncrypt($path, $mode); } diff --git a/apps/files_encryption/lib/session.php b/apps/files_encryption/lib/session.php index 93be6691f9..ef18b924dd 100644 --- a/apps/files_encryption/lib/session.php +++ b/apps/files_encryption/lib/session.php @@ -100,6 +100,8 @@ class Session { $privateKey = Crypt::decryptPrivateKey($encryptedKey, ''); $this->setPublicSharePrivateKey($privateKey); + $this->setInitialized(\OCA\Encryption\Session::INIT_SUCCESSFUL); + \OC_FileProxy::$enabled = $proxyStatus; } } diff --git a/apps/files_encryption/tests/webdav.php b/apps/files_encryption/tests/webdav.php index 84db54ff30..73bc9ce08d 100755 --- a/apps/files_encryption/tests/webdav.php +++ b/apps/files_encryption/tests/webdav.php @@ -235,7 +235,8 @@ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase { $view = new \OC\Files\View($root); $publicDir = new OC_Connector_Sabre_Directory($view, $view->getFileInfo('')); $objectTree = new \OC\Connector\Sabre\ObjectTree(); - $objectTree->init($publicDir, $view); + $mountManager = \OC\Files\Filesystem::getMountManager(); + $objectTree->init($publicDir, $view, $mountManager); // Fire up server $server = new \Sabre\DAV\Server($publicDir); diff --git a/apps/files_external/lib/webdav.php b/apps/files_external/lib/webdav.php index 525f41c127..c532c5eaa7 100644 --- a/apps/files_external/lib/webdav.php +++ b/apps/files_external/lib/webdav.php @@ -9,13 +9,13 @@ namespace OC\Files\Storage; class DAV extends \OC\Files\Storage\Common { - private $password; - private $user; - private $host; - private $secure; - private $root; - private $certPath; - private $ready; + protected $password; + protected $user; + protected $host; + protected $secure; + protected $root; + protected $certPath; + protected $ready; /** * @var \Sabre\DAV\Client */ @@ -355,6 +355,9 @@ class DAV extends \OC\Files\Storage\Common { * @param string $path */ public function cleanPath($path) { + if ($path === "") { + return $path; + } $path = \OC\Files\Filesystem::normalizePath($path); // remove leading slash return substr($path, 1); @@ -397,11 +400,27 @@ class DAV extends \OC\Files\Storage\Common { } } + public function isUpdatable($path) { + return (bool)($this->getPermissions($path) & \OCP\PERMISSION_UPDATE); + } + + public function isCreatable($path) { + return (bool)($this->getPermissions($path) & \OCP\PERMISSION_CREATE); + } + + public function isSharable($path) { + return (bool)($this->getPermissions($path) & \OCP\PERMISSION_SHARE); + } + + public function isDeletable($path) { + return (bool)($this->getPermissions($path) & \OCP\PERMISSION_DELETE); + } + public function getPermissions($path) { $this->init(); $response = $this->client->propfind($this->encodePath($path), array('{http://owncloud.org/ns}permissions')); if (isset($response['{http://owncloud.org/ns}permissions'])) { - $permissions = 0; + $permissions = \OCP\PERMISSION_READ; $permissionsString = $response['{http://owncloud.org/ns}permissions']; if (strpos($permissionsString, 'R') !== false) { $permissions |= \OCP\PERMISSION_SHARE; @@ -416,8 +435,12 @@ class DAV extends \OC\Files\Storage\Common { $permissions |= \OCP\PERMISSION_CREATE; } return $permissions; + } else if ($this->is_dir($path)) { + return \OCP\PERMISSION_ALL; + } else if ($this->file_exists($path)) { + return \OCP\PERMISSION_ALL - \OCP\PERMISSION_CREATE; } else { - return parent::getPermissions($path); + return 0; } } } diff --git a/apps/files_sharing/ajax/external.php b/apps/files_sharing/ajax/external.php new file mode 100644 index 0000000000..52c84b7bab --- /dev/null +++ b/apps/files_sharing/ajax/external.php @@ -0,0 +1,51 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +OCP\JSON::callCheck(); +OCP\JSON::checkLoggedIn(); +OCP\JSON::checkAppEnabled('files_sharing'); + +$l = OC_L10N::get('files_sharing'); + +// check if server admin allows to mount public links from other servers +// check if files_external is enabled +// FIXME file_external check no longer needed if we use the webdav implementation from core +if (OCA\Files_Sharing\Helper::isIncomingServer2serverShareEnabled() === false || + \OC_App::isEnabled('files_external') === false) { + \OCP\JSON::error(array('data' => array('message' => $l->t('Server to server sharing is not enabled on this server')))); + exit(); +} + +$token = $_POST['token']; +$remote = $_POST['remote']; +$owner = $_POST['owner']; +$name = $_POST['name']; +$password = $_POST['password']; + +$externalManager = new \OCA\Files_Sharing\External\Manager( + \OC::$server->getDatabaseConnection(), + \OC\Files\Filesystem::getMountManager(), + \OC\Files\Filesystem::getLoader(), + \OC::$server->getUserSession() +); + +$name = OCP\Files::buildNotExistingFileName('/', $name); + +$mount = $externalManager->addShare($remote, $token, $password, $name, $owner); +/** + * @var \OCA\Files_Sharing\External\Storage $storage + */ +$storage = $mount->getStorage(); +$result = $storage->file_exists(''); +if($result){ + $storage->getScanner()->scanAll(); + \OCP\JSON::success(); +} else { + $externalManager->removeShare($mount->getMountPoint()); + \OCP\JSON::error(array('data' => array('message' => $l->t("Couldn't add remote share")))); +} diff --git a/apps/files_sharing/ajax/shareinfo.php b/apps/files_sharing/ajax/shareinfo.php new file mode 100644 index 0000000000..e87b0779e8 --- /dev/null +++ b/apps/files_sharing/ajax/shareinfo.php @@ -0,0 +1,69 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +OCP\JSON::checkAppEnabled('files_sharing'); + +if (!isset($_GET['t'])) { + \OC_Response::setStatus(400); //400 Bad Request + exit; +} + +$token = $_GET['t']; + +$password = null; +if (isset($_POST['password'])) { + $password = $_POST['password']; +} + +$relativePath = null; +if (isset($_GET['dir'])) { + $relativePath = $_GET['dir']; +} + +$data = \OCA\Files_Sharing\Helper::setupFromToken($token, $relativePath, $password); + +$linkItem = $data['linkItem']; +// Load the files +$path = $data['realPath']; + +$isWritable = $linkItem['permissions'] & (\OCP\PERMISSION_UPDATE | \OCP\PERMISSION_CREATE); +if (!$isWritable) { + \OC\Files\Filesystem::addStorageWrapper('readonly', function ($mountPoint, $storage) { + return new \OCA\Files_Sharing\ReadOnlyWrapper(array('storage' => $storage)); + }); +} + +$rootInfo = \OC\Files\Filesystem::getFileInfo($path); +$rootView = new \OC\Files\View(''); + +/** + * @param \OCP\Files\FileInfo $dir + * @param \OC\Files\View $view + * @return array + */ +function getChildInfo($dir, $view) { + $children = $view->getDirectoryContent($dir->getPath()); + $result = array(); + foreach ($children as $child) { + $formated = \OCA\Files\Helper::formatFileInfo($child); + if ($child->getType() === 'dir') { + $formated['children'] = getChildInfo($child, $view); + } + $formated['mtime'] = $formated['mtime'] / 1000; + $result[] = $formated; + } + return $result; +} + +$result = \OCA\Files\Helper::formatFileInfo($rootInfo); +$result['mtime'] = $result['mtime'] / 1000; +if ($rootInfo->getType() === 'dir') { + $result['children'] = getChildInfo($rootInfo, $rootView); +} + +OCP\JSON::success(array('data' => $result)); diff --git a/apps/files_sharing/ajax/testremote.php b/apps/files_sharing/ajax/testremote.php new file mode 100644 index 0000000000..8958179469 --- /dev/null +++ b/apps/files_sharing/ajax/testremote.php @@ -0,0 +1,29 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +OCP\JSON::checkAppEnabled('files_sharing'); + +$remote = $_GET['remote']; + +function testUrl($url) { + try { + $result = file_get_contents($url); + $data = json_decode($result); + return is_object($data) and !empty($data->version); + } catch (Exception $e) { + return false; + } +} + +if (testUrl('https://' . $remote . '/status.php')) { + echo 'https'; +} elseif (testUrl('http://' . $remote . '/status.php')) { + echo 'http'; +} else { + echo 'false'; +} diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index 6b40ba921c..a9f4ff5089 100644 --- a/apps/files_sharing/appinfo/app.php +++ b/apps/files_sharing/appinfo/app.php @@ -11,10 +11,24 @@ OC::$CLASSPATH['OC\Files\Cache\Shared_Watcher'] = 'files_sharing/lib/watcher.php OC::$CLASSPATH['OCA\Files\Share\Api'] = 'files_sharing/lib/api.php'; OC::$CLASSPATH['OCA\Files\Share\Maintainer'] = 'files_sharing/lib/maintainer.php'; OC::$CLASSPATH['OCA\Files\Share\Proxy'] = 'files_sharing/lib/proxy.php'; + +\OCP\App::registerAdmin('files_sharing', 'settings-admin'); + +$externalManager = new \OCA\Files_Sharing\External\Manager( + \OC::$server->getDatabaseConnection(), + \OC\Files\Filesystem::getMountManager(), + \OC\Files\Filesystem::getLoader(), + \OC::$server->getUserSession() +); +$externalManager->setup(); + OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup'); OCP\Share::registerBackend('file', 'OC_Share_Backend_File'); OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file'); + OCP\Util::addScript('files_sharing', 'share'); +OCP\Util::addScript('files_sharing', 'external'); + \OC_Hook::connect('OC_Filesystem', 'post_write', '\OC\Files\Cache\Shared_Updater', 'writeHook'); \OC_Hook::connect('OC_Filesystem', 'post_delete', '\OC\Files\Cache\Shared_Updater', 'postDeleteHook'); \OC_Hook::connect('OC_Filesystem', 'delete', '\OC\Files\Cache\Shared_Updater', 'deleteHook'); diff --git a/apps/files_sharing/appinfo/database.xml b/apps/files_sharing/appinfo/database.xml new file mode 100644 index 0000000000..73d64c527b --- /dev/null +++ b/apps/files_sharing/appinfo/database.xml @@ -0,0 +1,95 @@ + + + *dbname* + true + false + utf8 + + *dbprefix*share_external + + + id + integer + 0 + true + 1 + 4 + + + remote + text + true + 512 + Url of the remove owncloud instance + + + share_token + text + true + 64 + Public share token + + + password + text + true + 64 + Optional password for the public share + + + name + text + true + 64 + Original name on the remote server + + + owner + text + true + 64 + User that owns the public share on the remote server + + + user + text + true + 64 + Local user which added the external share + + + mountpoint + text + true + 4000 + Full path where the share is mounted + + + mountpoint_hash + text + true + 32 + md5 hash of the mountpoint + + + sh_external_user + + user + ascending + + + + sh_external_mp + true + + user + ascending + + + mountpoint_hash + ascending + + + +
+
diff --git a/apps/files_sharing/appinfo/routes.php b/apps/files_sharing/appinfo/routes.php index 7c2834dc9c..e68b953fc0 100644 --- a/apps/files_sharing/appinfo/routes.php +++ b/apps/files_sharing/appinfo/routes.php @@ -5,6 +5,10 @@ $this->create('core_ajax_public_preview', '/publicpreview')->action( require_once __DIR__ . '/../ajax/publicpreview.php'; }); +$this->create('sharing_external_shareinfo', '/shareinfo')->actionInclude('files_sharing/ajax/shareinfo.php'); +$this->create('sharing_external_add', '/external')->actionInclude('files_sharing/ajax/external.php'); +$this->create('sharing_external_test_remote', '/testremote')->actionInclude('files_sharing/ajax/testremote.php'); + // OCS API //TODO: SET: mail notification, waiting for PR #4689 to be accepted diff --git a/apps/files_sharing/appinfo/version b/apps/files_sharing/appinfo/version index 2eb3c4fe4e..cb0c939a93 100644 --- a/apps/files_sharing/appinfo/version +++ b/apps/files_sharing/appinfo/version @@ -1 +1 @@ -0.5 +0.5.2 diff --git a/apps/files_sharing/css/public.css b/apps/files_sharing/css/public.css index 1bafb78074..31c3bca874 100644 --- a/apps/files_sharing/css/public.css +++ b/apps/files_sharing/css/public.css @@ -87,3 +87,38 @@ thead { width: 300px; max-width: 90%; } + +.header-right { + transition: opacity 500ms ease 0s; + -moz-transition: opacity 500ms ease 0s; + -ms-transition: opacity 500ms ease 0s; + -o-transition: opacity 500ms ease 0s; + -webkit-transition: opacity 500ms ease 0s; +} + +.header-right:hover, .header-right.active { + opacity: 1; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; + filter: alpha(opacity=100); +} + +/* within #save */ +#remote_address { + margin: 0; + height: 14px; + line-height: 16px; + padding: 6px; +} + +#save button { + margin: 0 5px; + height: 28px; + padding-bottom: 4px; + line-height: 14px; +} + +#save .save-form [type="submit"] { + margin: 0 5px; + height: 28px; + padding-bottom: 4px; +} diff --git a/apps/files_sharing/js/external.js b/apps/files_sharing/js/external.js new file mode 100644 index 0000000000..5c476b2d43 --- /dev/null +++ b/apps/files_sharing/js/external.js @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014 Robin Appelman + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ +(function () { + var getParameterByName = function (query, name) { + name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + var regex = new RegExp("[\\#&]" + name + "=([^&#]*)"), + results = regex.exec(query); + return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); + }; + + var addExternalShare = function (remote, token, owner, name, password) { + return $.post(OC.generateUrl('apps/files_sharing/external'), { + remote: remote, + token: token, + owner: owner, + name: name, + password: password + }); + }; + + var showAddExternalDialog = function (remote, token, owner, name, passwordProtected) { + var remoteClean = (remote.substr(0, 8) === 'https://') ? remote.substr(8) : remote.substr(7); + var callback = function (add, password) { + password = password || ''; + if (add) { + addExternalShare(remote, token, owner, name, password).then(function (result) { + if (result.status === 'error') { + OC.Notification.show(result.data.message); + } else { + FileList.reload(); + } + }); + } + }; + if (!passwordProtected) { + OC.dialogs.confirm(t('files_sharing', 'Add {name} from {owner}@{remote}', {name: name, owner: owner, remote: remoteClean}) + , 'Add Share', callback, true); + } else { + OC.dialogs.prompt(t('files_sharing', 'Add {name} from {owner}@{remote}', {name: name, owner: owner, remote: remoteClean}) + , 'Add Share', callback, true, 'Password', true); + } + }; + + OCA.Sharing.showAddExternalDialog = function (hash) { + var remote = getParameterByName(hash, 'remote'); + var owner = getParameterByName(hash, 'owner'); + var name = getParameterByName(hash, 'name'); + var token = getParameterByName(hash, 'token'); + var passwordProtected = parseInt(getParameterByName(hash, 'protected'), 10); + + if (remote && token && owner && name) { + showAddExternalDialog(remote, token, owner, name, passwordProtected); + } + }; +})(); + +$(document).ready(function () { + // FIXME: HACK: do not init when running unit tests, need a better way + if (!window.TESTING && OCA.Files) {// only run in the files app + var hash = location.hash; + location.hash = ''; + OCA.Sharing.showAddExternalDialog(hash); + } +}); diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js index a2248405d2..80631908d2 100644 --- a/apps/files_sharing/js/public.js +++ b/apps/files_sharing/js/public.js @@ -19,7 +19,7 @@ if (!OCA.Files) { OCA.Sharing.PublicApp = { _initialized: false, - initialize: function($el) { + initialize: function ($el) { var self = this; var fileActions; if (this._initialized) { @@ -65,7 +65,7 @@ OCA.Sharing.PublicApp = { } // dynamically load image previews - if (mimetype.substr(0, mimetype.indexOf('/')) === 'image' ) { + if (mimetype.substr(0, mimetype.indexOf('/')) === 'image') { var params = { x: $(document).width() * window.devicePixelRatio, @@ -82,7 +82,7 @@ OCA.Sharing.PublicApp = { if (this.fileList) { // TODO: move this to a separate PublicFileList class that extends OCA.Files.FileList (+ unit tests) - this.fileList.getDownloadUrl = function(filename, dir) { + this.fileList.getDownloadUrl = function (filename, dir) { if ($.isArray(filename)) { filename = JSON.stringify(filename); } @@ -97,13 +97,13 @@ OCA.Sharing.PublicApp = { return OC.filePath('', '', 'public.php') + '?' + OC.buildQueryString(params); }; - this.fileList.getAjaxUrl = function(action, params) { + this.fileList.getAjaxUrl = function (action, params) { params = params || {}; params.t = $('#sharingToken').val(); return OC.filePath('files_sharing', 'ajax', action + '.php') + '?' + OC.buildQueryString(params); }; - this.fileList.linkTo = function(dir) { + this.fileList.linkTo = function (dir) { var params = { service: 'files', t: $('#sharingToken').val(), @@ -112,15 +112,15 @@ OCA.Sharing.PublicApp = { return OC.filePath('', '', 'public.php') + '?' + OC.buildQueryString(params); }; - this.fileList.generatePreviewUrl = function(urlSpec) { + this.fileList.generatePreviewUrl = function (urlSpec) { urlSpec.t = $('#dirToken').val(); return OC.generateUrl('/apps/files_sharing/ajax/publicpreview.php?') + $.param(urlSpec); }; var file_upload_start = $('#file_upload_start'); - file_upload_start.on('fileuploadadd', function(e, data) { + file_upload_start.on('fileuploadadd', function (e, data) { var fileDirectory = ''; - if(typeof data.files[0].relativePath !== 'undefined') { + if (typeof data.files[0].relativePath !== 'undefined') { fileDirectory = data.files[0].relativePath; } @@ -143,16 +143,34 @@ OCA.Sharing.PublicApp = { OC.Util.History.addOnPopStateHandler(_.bind(this._onUrlChanged, this)); } - $(document).on('click', '#directLink', function() { + $(document).on('click', '#directLink', function () { $(this).focus(); $(this).select(); }); + $('.save-form').submit(function (event) { + event.preventDefault(); + + var remote = $(this).find('input[type="text"]').val(); + var token = $('#sharingToken').val(); + var owner = $('#save').data('owner'); + var name = $('#save').data('name'); + var isProtected = $('#save').data('protected') ? 1 : 0; + OCA.Sharing.PublicApp._saveToOwnCloud(remote, token, owner, name, isProtected); + }); + + $('#save > button').click(function () { + $(this).hide(); + $('.header-right').addClass('active'); + $('.save-form').css('display', 'inline'); + $('#remote_address').focus(); + }); + // legacy window.FileList = this.fileList; }, - _onDirectoryChanged: function(e) { + _onDirectoryChanged: function (e) { OC.Util.History.pushState({ service: 'files', t: $('#sharingToken').val(), @@ -161,21 +179,44 @@ OCA.Sharing.PublicApp = { }); }, - _onUrlChanged: function(params) { + _onUrlChanged: function (params) { this.fileList.changeDirectory(params.path || params.dir, false, true); + }, + + _saveToOwnCloud: function(remote, token, owner, name, isProtected) { + var location = window.location.protocol + '//' + window.location.host + OC.webroot; + + var url = remote + '/index.php/apps/files#' + 'remote=' + encodeURIComponent(location) // our location is the remote for the other server + + "&token=" + encodeURIComponent(token) + "&owner=" + encodeURIComponent(owner) + "&name=" + encodeURIComponent(name) + "&protected=" + isProtected; + + + if (remote.indexOf('://') > 0) { + OC.redirect(url); + } else { + // if no protocol is specified, we automatically detect it by testing https and http + // this check needs to happen on the server due to the Content Security Policy directive + $.get(OC.generateUrl('apps/files_sharing/testremote'), {remote: remote}).then(function (protocol) { + if (protocol !== 'http' && protocol !== 'https') { + OC.dialogs.alert(t('files_sharing', 'No ownCloud installation found at {remote}', {remote: remote}), + t('files_sharing', 'Invalid ownCloud url')); + } else { + OC.redirect(protocol + '://' + url); + } + }); + } } }; -$(document).ready(function() { +$(document).ready(function () { var App = OCA.Sharing.PublicApp; // defer app init, to give a chance to plugins to register file actions - _.defer(function() { + _.defer(function () { App.initialize($('#preview')); }); if (window.Files) { // HACK: for oc-dialogs previews that depends on Files: - Files.lazyLoadPreview = function(path, mime, ready, width, height, etag) { + Files.lazyLoadPreview = function (path, mime, ready, width, height, etag) { return App.fileList.lazyLoadPreview({ path: path, mime: mime, diff --git a/apps/files_sharing/js/settings-admin.js b/apps/files_sharing/js/settings-admin.js new file mode 100644 index 0000000000..257c864b04 --- /dev/null +++ b/apps/files_sharing/js/settings-admin.js @@ -0,0 +1,11 @@ +$(document).ready(function() { + + $('#fileSharingSettings input').change(function() { + var value = 'no'; + if (this.checked) { + value = 'yes'; + } + OC.AppConfig.setValue('files_sharing', $(this).attr('name'), value); + }); + +}); diff --git a/apps/files_sharing/lib/connector/publicauth.php b/apps/files_sharing/lib/connector/publicauth.php index ec7b68ba69..c9d545180b 100644 --- a/apps/files_sharing/lib/connector/publicauth.php +++ b/apps/files_sharing/lib/connector/publicauth.php @@ -38,6 +38,7 @@ class PublicAuth extends \Sabre\DAV\Auth\Backend\AbstractBasic { */ protected function validateUserPass($username, $password) { $linkItem = \OCP\Share::getShareByToken($username, false); + \OC_User::setIncognitoMode(true); $this->share = $linkItem; if (!$linkItem) { return false; diff --git a/apps/files_sharing/lib/external/cache.php b/apps/files_sharing/lib/external/cache.php new file mode 100644 index 0000000000..cd06bfb127 --- /dev/null +++ b/apps/files_sharing/lib/external/cache.php @@ -0,0 +1,47 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCA\Files_Sharing\External; + +class Cache extends \OC\Files\Cache\Cache { + private $remote; + private $remoteUser; + private $storage; + + /** + * @param \OCA\Files_Sharing\External\Storage $storage + * @param string $remote + * @param string $remoteUser + */ + public function __construct($storage, $remote, $remoteUser) { + $this->storage = $storage; + list(, $remote) = explode('://', $remote, 2); + $this->remote = $remote; + $this->remoteUser = $remoteUser; + parent::__construct($storage); + } + + public function get($file) { + $result = parent::get($file); + $result['displayname_owner'] = $this->remoteUser . '@' . $this->remote; + if (!$file || $file === '') { + $result['is_share_mount_point'] = true; + $mountPoint = rtrim($this->storage->getMountPoint()); + $result['name'] = basename($mountPoint); + } + return $result; + } + + public function getFolderContentsById($id) { + $results = parent::getFolderContentsById($id); + foreach ($results as &$file) { + $file['displayname_owner'] = $this->remoteUser . '@' . $this->remote; + } + return $results; + } +} diff --git a/apps/files_sharing/lib/external/manager.php b/apps/files_sharing/lib/external/manager.php new file mode 100644 index 0000000000..70a0e98ebd --- /dev/null +++ b/apps/files_sharing/lib/external/manager.php @@ -0,0 +1,140 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCA\Files_Sharing\External; + +use OC\Files\Filesystem; + +class Manager { + const STORAGE = '\OCA\Files_Sharing\External\Storage'; + + /** + * @var \OCP\IDBConnection + */ + private $connection; + + /** + * @var \OC\Files\Mount\Manager + */ + private $mountManager; + + /** + * @var \OC\Files\Storage\Loader + */ + private $storageLoader; + + /** + * @var \OC\User\Session + */ + private $userSession; + + /** + * @param \OCP\IDBConnection $connection + * @param \OC\Files\Mount\Manager $mountManager + * @param \OC\User\Session $userSession + * @param \OC\Files\Storage\Loader $storageLoader + */ + public function __construct(\OCP\IDBConnection $connection, \OC\Files\Mount\Manager $mountManager, + \OC\Files\Storage\Loader $storageLoader, \OC\User\Session $userSession) { + $this->connection = $connection; + $this->mountManager = $mountManager; + $this->userSession = $userSession; + $this->storageLoader = $storageLoader; + } + + public function addShare($remote, $token, $password, $name, $owner) { + $user = $this->userSession->getUser(); + if ($user) { + $query = $this->connection->prepare('INSERT INTO *PREFIX*share_external(`remote`, `share_token`, `password`, + `name`, `owner`, `user`, `mountpoint`, `mountpoint_hash`) VALUES(?, ?, ?, ?, ?, ?, ?, ?)'); + $mountPoint = Filesystem::normalizePath('/' . $name); + $hash = md5($mountPoint); + $query->execute(array($remote, $token, $password, $name, $owner, $user->getUID(), $mountPoint, $hash)); + + $options = array( + 'remote' => $remote, + 'token' => $token, + 'password' => $password, + 'mountpoint' => $mountPoint, + 'owner' => $owner + ); + return $this->mountShare($options); + } + } + + public function setup() { + // don't setup server-to-server shares if the file_external app is disabled + // FIXME no longer needed if we use the webdav implementation from core + if (\OC_App::isEnabled('files_external') === false) { + return false; + } + + $user = $this->userSession->getUser(); + if ($user) { + $query = $this->connection->prepare('SELECT `remote`, `share_token`, `password`, `mountpoint`, `owner` + FROM *PREFIX*share_external WHERE `user` = ?'); + $query->execute(array($user->getUID())); + + while ($row = $query->fetch()) { + $row['manager'] = $this; + $row['token'] = $row['share_token']; + $this->mountShare($row); + } + } + } + + protected function stripPath($path) { + $prefix = '/' . $this->userSession->getUser()->getUID() . '/files'; + return rtrim(substr($path, strlen($prefix)), '/'); + } + + /** + * @param array $data + * @return Mount + */ + protected function mountShare($data) { + $mountPoint = '/' . $this->userSession->getUser()->getUID() . '/files' . $data['mountpoint']; + $mount = new Mount(self::STORAGE, $mountPoint, $data, $this, $this->storageLoader); + $this->mountManager->addMount($mount); + return $mount; + } + + /** + * @return \OC\Files\Mount\Manager + */ + public function getMountManager() { + return $this->mountManager; + } + + /** + * @param string $source + * @param string $target + * @return bool + */ + public function setMountPoint($source, $target) { + $user = $this->userSession->getUser(); + $source = $this->stripPath($source); + $target = $this->stripPath($target); + $sourceHash = md5($source); + $targetHash = md5($target); + + $query = $this->connection->prepare('UPDATE *PREFIX*share_external SET + `mountpoint` = ?, `mountpoint_hash` = ? WHERE `mountpoint_hash` = ? AND `user` = ?'); + $result = (bool)$query->execute(array($target, $targetHash, $sourceHash, $user->getUID())); + + return $result; + } + + public function removeShare($mountPoint) { + $user = $this->userSession->getUser(); + $mountPoint = $this->stripPath($mountPoint); + $hash = md5($mountPoint); + $query = $this->connection->prepare('DELETE FROM *PREFIX*share_external WHERE `mountpoint_hash` = ? AND `user` = ?'); + return (bool)$query->execute(array($hash, $user->getUID())); + } +} diff --git a/apps/files_sharing/lib/external/mount.php b/apps/files_sharing/lib/external/mount.php new file mode 100644 index 0000000000..a42a12f9b9 --- /dev/null +++ b/apps/files_sharing/lib/external/mount.php @@ -0,0 +1,53 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCA\Files_Sharing\External; + +use OC\Files\Mount\MoveableMount; + +class Mount extends \OC\Files\Mount\Mount implements MoveableMount { + + /** + * @var \OCA\Files_Sharing\External\Manager + */ + protected $manager; + + /** + * @param string|\OC\Files\Storage\Storage $storage + * @param string $mountpoint + * @param array $options + * @param \OCA\Files_Sharing\External\Manager $manager + * @param \OC\Files\Storage\Loader $loader + */ + public function __construct($storage, $mountpoint, $options, $manager, $loader = null) { + parent::__construct($storage, $mountpoint, $options, $loader); + $this->manager = $manager; + } + + /** + * Move the mount point to $target + * + * @param string $target the target mount point + * @return bool + */ + public function moveMount($target) { + $result = $this->manager->setMountPoint($this->mountPoint, $target); + $this->setMountPoint($target); + return $result; + } + + /** + * Remove the mount points + * + * @return mixed + * @return bool + */ + public function removeMount() { + return $this->manager->removeShare($this->mountPoint); + } +} diff --git a/apps/files_sharing/lib/external/scanner.php b/apps/files_sharing/lib/external/scanner.php new file mode 100644 index 0000000000..8921dd1a4c --- /dev/null +++ b/apps/files_sharing/lib/external/scanner.php @@ -0,0 +1,54 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCA\Files_Sharing\External; + +class Scanner extends \OC\Files\Cache\Scanner { + /** + * @var \OCA\Files_Sharing\External\Storage + */ + protected $storage; + + public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1) { + $this->scanAll(); + } + + public function scanAll() { + $remote = $this->storage->getRemote(); + $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') { + $this->addResult($data['data'], ''); + } else { + throw new \Exception('Error while scanning remote share'); + } + } + + private function addResult($data, $path) { + $this->cache->put($path, $data); + if (isset($data['children'])) { + foreach ($data['children'] as $child) { + $this->addResult($child, ltrim($path . '/' . $child['name'], '/')); + } + } + } +} diff --git a/apps/files_sharing/lib/external/storage.php b/apps/files_sharing/lib/external/storage.php new file mode 100644 index 0000000000..cd04841bb0 --- /dev/null +++ b/apps/files_sharing/lib/external/storage.php @@ -0,0 +1,103 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCA\Files_Sharing\External; + +use OC\Files\Filesystem; +use OC\Files\Storage\DAV; +use OCA\Files_Sharing\ISharedStorage; + +class Storage extends DAV implements ISharedStorage { + /** + * @var string + */ + private $remoteUser; + + /** + * @var string + */ + private $remote; + + /** + * @var string + */ + private $mountPoint; + + /** + * @var string + */ + private $token; + + public function __construct($options) { + $this->remote = $options['remote']; + $this->remoteUser = $options['owner']; + list($protocol, $remote) = explode('://', $this->remote); + list($host, $root) = explode('/', $remote); + $secure = $protocol === 'https'; + $root .= '/public.php/webdav'; + $this->mountPoint = $options['mountpoint']; + $this->token = $options['token']; + parent::__construct(array( + 'secure' => $secure, + 'host' => $host, + 'root' => $root, + 'user' => $options['token'], + 'password' => $options['password'] + )); + } + + public function getRemoteUser() { + return $this->remoteUser; + } + + public function getRemote() { + return $this->remote; + } + + public function getMountPoint() { + return $this->mountPoint; + } + + public function getToken() { + return $this->token; + } + + public function getPassword() { + return $this->password; + } + + /** + * @brief get id of the mount point + * @return string + */ + public function getId() { + return 'shared::' . md5($this->token . '@' . $this->remote); + } + + public function getCache($path = '', $storage = null) { + if (!$storage) { + $this->cache = new Cache($this, $this->remote, $this->remoteUser); + } + return $this->cache; + } + + /** + * @param string $path + * @param \OC\Files\Storage\Storage $storage + * @return \OCA\Files_Sharing\External\Scanner + */ + public function getScanner($path = '', $storage = null) { + if (!$storage) { + $storage = $this; + } + if (!isset($this->scanner)) { + $this->scanner = new Scanner($storage); + } + return $this->scanner; + } +} diff --git a/apps/files_sharing/lib/helper.php b/apps/files_sharing/lib/helper.php index 49546f012a..34de3a915a 100644 --- a/apps/files_sharing/lib/helper.php +++ b/apps/files_sharing/lib/helper.php @@ -16,7 +16,7 @@ class Helper { public static function setupFromToken($token, $relativePath = null, $password = null) { \OC_User::setIncognitoMode(true); - $linkItem = \OCP\Share::getShareByToken($token); + $linkItem = \OCP\Share::getShareByToken($token, !$password); if($linkItem === false || ($linkItem['item_type'] !== 'file' && $linkItem['item_type'] !== 'folder')) { \OC_Response::setStatus(404); \OC_Log::write('core-preview', 'Passed token parameter is not valid', \OC_Log::DEBUG); @@ -202,4 +202,24 @@ class Helper { return $path; } + + /** + * allow users from other ownCloud instances to mount public links share by this instance + * @return bool + */ + public static function isOutgoingServer2serverShareEnabled() { + $appConfig = \OC::$server->getAppConfig(); + $result = $appConfig->getValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes'); + return ($result === 'yes') ? true : false; + } + + /** + * allow user to mount public links from onther ownClouds + * @return bool + */ + public static function isIncomingServer2serverShareEnabled() { + $appConfig = \OC::$server->getAppConfig(); + $result = $appConfig->getValue('files_sharing', 'incoming_server2server_share_enabled', 'yes'); + return ($result === 'yes') ? true : false; + } } diff --git a/apps/files_sharing/lib/isharedstorage.php b/apps/files_sharing/lib/isharedstorage.php new file mode 100644 index 0000000000..75e0afef39 --- /dev/null +++ b/apps/files_sharing/lib/isharedstorage.php @@ -0,0 +1,13 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCA\Files_Sharing; + +interface ISharedStorage{ + +} diff --git a/apps/files_sharing/lib/readonlycache.php b/apps/files_sharing/lib/readonlycache.php new file mode 100644 index 0000000000..f129ca4943 --- /dev/null +++ b/apps/files_sharing/lib/readonlycache.php @@ -0,0 +1,27 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCA\Files_Sharing; + +use OC\Files\Cache\Cache; + +class ReadOnlyCache extends Cache { + public function get($path) { + $data = parent::get($path); + $data['permissions'] &= (\OCP\PERMISSION_READ | \OCP\PERMISSION_SHARE); + return $data; + } + + public function getFolderContents($path) { + $content = parent::getFolderContents($path); + foreach ($content as &$data) { + $data['permissions'] &= (\OCP\PERMISSION_READ | \OCP\PERMISSION_SHARE); + } + return $content; + } +} diff --git a/apps/files_sharing/lib/readonlywrapper.php b/apps/files_sharing/lib/readonlywrapper.php new file mode 100644 index 0000000000..45ed3fd68b --- /dev/null +++ b/apps/files_sharing/lib/readonlywrapper.php @@ -0,0 +1,56 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCA\Files_Sharing; + +use OC\Files\Storage\Wrapper\Wrapper; + +class ReadOnlyWrapper extends Wrapper { + public function isUpdatable($path) { + return false; + } + + public function isCreatable($path) { + return false; + } + + public function isDeletable($path) { + return false; + } + + public function getPermissions($path) { + return $this->storage->getPermissions($path) & (\OCP\PERMISSION_READ | \OCP\PERMISSION_SHARE); + } + + public function rename($path1, $path2) { + return false; + } + + public function touch($path, $mtime = null) { + return false; + } + + public function mkdir($path) { + return false; + } + + public function rmdir($path) { + return false; + } + + public function unlink($path) { + return false; + } + + public function getCache($path = '', $storage = null) { + if (!$storage) { + $storage = $this; + } + return new ReadOnlyCache($storage); + } +} diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index 59de2dfa4c..8d5b22dc28 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -23,12 +23,13 @@ namespace OC\Files\Storage; use OC\Files\Filesystem; +use OCA\Files_Sharing\ISharedStorage; use OCA\Files_Sharing\SharedMount; /** * Convert target path to source path and pass the function call to the correct storage provider */ -class Shared extends \OC\Files\Storage\Common { +class Shared extends \OC\Files\Storage\Common implements ISharedStorage { private $share; // the shared resource private $files = array(); @@ -488,16 +489,25 @@ class Shared extends \OC\Files\Storage\Common { return $this->filemtime($path) > $time; } - public function getCache($path = '') { - return new \OC\Files\Cache\Shared_Cache($this); + public function getCache($path = '', $storage = null) { + if (!$storage) { + $storage = $this; + } + return new \OC\Files\Cache\Shared_Cache($storage); } - public function getScanner($path = '') { - return new \OC\Files\Cache\Scanner($this); + public function getScanner($path = '', $storage = null) { + if (!$storage) { + $storage = $this; + } + return new \OC\Files\Cache\Scanner($storage); } - public function getWatcher($path = '') { - return new \OC\Files\Cache\Shared_Watcher($this); + public function getWatcher($path = '', $storage = null) { + if (!$storage) { + $storage = $this; + } + return new \OC\Files\Cache\Shared_Watcher($storage); } public function getOwner($path) { diff --git a/apps/files_sharing/public.php b/apps/files_sharing/public.php index 4782c4dbe3..ec7c80f331 100644 --- a/apps/files_sharing/public.php +++ b/apps/files_sharing/public.php @@ -128,6 +128,7 @@ if (isset($path)) { $tmpl->assign('mimetype', \OC\Files\Filesystem::getMimeType($path)); $tmpl->assign('dirToken', $linkItem['token']); $tmpl->assign('sharingToken', $token); + $tmpl->assign('protected', isset($linkItem['share_with']) ? 'true' : 'false'); $urlLinkIdentifiers= (isset($token)?'&t='.$token:'') .(isset($_GET['dir'])?'&dir='.$_GET['dir']:'') diff --git a/apps/files_sharing/publicwebdav.php b/apps/files_sharing/publicwebdav.php index df2c04cf45..684edd9767 100644 --- a/apps/files_sharing/publicwebdav.php +++ b/apps/files_sharing/publicwebdav.php @@ -6,6 +6,10 @@ * See the COPYING-README file. */ +if (OCA\Files_Sharing\Helper::isOutgoingServer2serverShareEnabled() === false) { + return false; +} + // load needed apps $RUNTIME_APPTYPES = array('filesystem', 'authentication', 'logging'); @@ -37,7 +41,15 @@ $server->addPlugin(new OC_Connector_Sabre_ExceptionLoggerPlugin('webdav')); $server->subscribeEvent('beforeMethod', function () use ($server, $objectTree, $authBackend) { $share = $authBackend->getShare(); $owner = $share['uid_owner']; + $isWritable = $share['permissions'] & (\OCP\PERMISSION_UPDATE | \OCP\PERMISSION_CREATE); $fileId = $share['file_source']; + + if (!$isWritable) { + \OC\Files\Filesystem::addStorageWrapper('readonly', function ($mountPoint, $storage) { + return new \OCA\Files_Sharing\ReadOnlyWrapper(array('storage' => $storage)); + }); + } + OC_Util::setupFS($owner); $ownerView = \OC\Files\Filesystem::getView(); $path = $ownerView->getPath($fileId); @@ -47,8 +59,13 @@ $server->subscribeEvent('beforeMethod', function () use ($server, $objectTree, $ $rootInfo = $view->getFileInfo(''); // Create ownCloud Dir - $rootDir = new OC_Connector_Sabre_Directory($view, $rootInfo); - $objectTree->init($rootDir, $view); + if ($rootInfo->getType() === 'dir') { + $root = new OC_Connector_Sabre_Directory($view, $rootInfo); + } else { + $root = new OC_Connector_Sabre_File($view, $rootInfo); + } + $mountManager = \OC\Files\Filesystem::getMountManager(); + $objectTree->init($root, $view, $mountManager); $server->addPlugin(new OC_Connector_Sabre_AbortedUploadDetectionPlugin($view)); $server->addPlugin(new OC_Connector_Sabre_QuotaPlugin($view)); diff --git a/apps/files_sharing/settings-admin.php b/apps/files_sharing/settings-admin.php new file mode 100644 index 0000000000..9c630b6a91 --- /dev/null +++ b/apps/files_sharing/settings-admin.php @@ -0,0 +1,17 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +\OC_Util::checkAdminUser(); + +\OCP\Util::addScript('files_sharing', 'settings-admin'); + +$tmpl = new OCP\Template('files_sharing', 'settings-admin'); +$tmpl->assign('outgoingServer2serverShareEnabled', OCA\Files_Sharing\Helper::isOutgoingServer2serverShareEnabled()); +$tmpl->assign('incomingServer2serverShareEnabled', OCA\Files_Sharing\Helper::isIncomingServer2serverShareEnabled()); + +return $tmpl->fetchPage(); diff --git a/apps/files_sharing/templates/public.php b/apps/files_sharing/templates/public.php index 7b5f603a10..c053aaabec 100644 --- a/apps/files_sharing/templates/public.php +++ b/apps/files_sharing/templates/public.php @@ -15,10 +15,19 @@ src="" alt="getName()); ?>" />
- - "/> - t('Download'))?> - + + + + + + + "/> + t('Download'))?> + +
diff --git a/apps/files_sharing/templates/settings-admin.php b/apps/files_sharing/templates/settings-admin.php new file mode 100644 index 0000000000..18cf227670 --- /dev/null +++ b/apps/files_sharing/templates/settings-admin.php @@ -0,0 +1,13 @@ +
+ +

t('File Sharing'));?>

+ + /> +
+ + /> +
+ +
diff --git a/config/.htaccess b/config/.htaccess old mode 100644 new mode 100755 diff --git a/lib/private/connector/sabre/directory.php b/lib/private/connector/sabre/directory.php index aa467cec53..9904c3525c 100644 --- a/lib/private/connector/sabre/directory.php +++ b/lib/private/connector/sabre/directory.php @@ -202,7 +202,8 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node * @return array */ public function getQuotaInfo() { - $storageInfo = OC_Helper::getStorageInfo($this->path); + $path = \OC\Files\Filesystem::getView()->getRelativePath($this->info->getPath()); + $storageInfo = OC_Helper::getStorageInfo($path); return array( $storageInfo['used'], $storageInfo['free'] diff --git a/lib/private/connector/sabre/file.php b/lib/private/connector/sabre/file.php index 4e90d46ad4..7591cc5c06 100644 --- a/lib/private/connector/sabre/file.php +++ b/lib/private/connector/sabre/file.php @@ -140,7 +140,7 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ if (\OC_Util::encryptedFiles()) { throw new \Sabre\DAV\Exception\ServiceUnavailable(); } else { - return $this->fileView->fopen($this->path, 'rb'); + return $this->fileView->fopen(ltrim($this->path, '/'), 'rb'); } } diff --git a/lib/private/connector/sabre/objecttree.php b/lib/private/connector/sabre/objecttree.php index c55a392bca..f2578e3c09 100644 --- a/lib/private/connector/sabre/objecttree.php +++ b/lib/private/connector/sabre/objecttree.php @@ -10,6 +10,7 @@ namespace OC\Connector\Sabre; use OC\Files\FileInfo; use OC\Files\Filesystem; +use OC\Files\Mount\MoveableMount; class ObjectTree extends \Sabre\DAV\ObjectTree { @@ -18,6 +19,11 @@ class ObjectTree extends \Sabre\DAV\ObjectTree { */ protected $fileView; + /** + * @var \OC\Files\Mount\Manager + */ + protected $mountManager; + /** * Creates the object * @@ -29,10 +35,12 @@ class ObjectTree extends \Sabre\DAV\ObjectTree { /** * @param \Sabre\DAV\ICollection $rootNode * @param \OC\Files\View $view + * @param \OC\Files\Mount\Manager $mountManager */ - public function init(\Sabre\DAV\ICollection $rootNode, \OC\Files\View $view) { + public function init(\Sabre\DAV\ICollection $rootNode, \OC\Files\View $view, \OC\Files\Mount\Manager $mountManager) { $this->rootNode = $rootNode; $this->fileView = $view; + $this->mountManager = $mountManager; } /** @@ -115,14 +123,15 @@ class ObjectTree extends \Sabre\DAV\ObjectTree { list($sourceDir,) = \Sabre\DAV\URLUtil::splitPath($sourcePath); list($destinationDir,) = \Sabre\DAV\URLUtil::splitPath($destinationPath); - $isShareMountPoint = false; - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath( '/' . \OCP\User::getUser() . '/files/' . $sourcePath); - if ($storage instanceof \OC\Files\Storage\Shared && !$internalPath) { - $isShareMountPoint = true; + $isMovableMount = false; + $sourceMount = $this->mountManager->find($this->fileView->getAbsolutePath($sourcePath)); + $internalPath = $sourceMount->getInternalPath($this->fileView->getAbsolutePath($sourcePath)); + if ($sourceMount instanceof MoveableMount && $internalPath === '') { + $isMovableMount = true; } // check update privileges - if (!$this->fileView->isUpdatable($sourcePath) && !$isShareMountPoint) { + if (!$this->fileView->isUpdatable($sourcePath) && !$isMovableMount) { throw new \Sabre\DAV\Exception\Forbidden(); } if ($sourceDir !== $destinationDir) { @@ -132,7 +141,7 @@ class ObjectTree extends \Sabre\DAV\ObjectTree { if (!$this->fileView->isUpdatable($destinationDir)) { throw new \Sabre\DAV\Exception\Forbidden(); } - if (!$this->fileView->isDeletable($sourcePath)) { + if (!$this->fileView->isDeletable($sourcePath) && !$isMovableMount) { throw new \Sabre\DAV\Exception\Forbidden(); } } diff --git a/lib/private/files/cache/cache.php b/lib/private/files/cache/cache.php index bfd280a91a..48c57e2e43 100644 --- a/lib/private/files/cache/cache.php +++ b/lib/private/files/cache/cache.php @@ -22,20 +22,20 @@ class Cache { /** * @var array partial data for the cache */ - private $partial = array(); + protected $partial = array(); /** * @var string */ - private $storageId; + protected $storageId; /** * @var Storage $storageCache */ - private $storageCache; + protected $storageCache; - private static $mimetypeIds = array(); - private static $mimetypes = array(); + protected static $mimetypeIds = array(); + protected static $mimetypes = array(); /** * @param \OC\Files\Storage\Storage|string $storage diff --git a/lib/private/files/storage/common.php b/lib/private/files/storage/common.php index 4d5a2078ef..ecc75298b6 100644 --- a/lib/private/files/storage/common.php +++ b/lib/private/files/storage/common.php @@ -287,31 +287,43 @@ abstract class Common implements \OC\Files\Storage\Storage { return $this->filemtime($path) > $time; } - public function getCache($path = '') { + public function getCache($path = '', $storage = null) { + if (!$storage) { + $storage = $this; + } if (!isset($this->cache)) { - $this->cache = new \OC\Files\Cache\Cache($this); + $this->cache = new \OC\Files\Cache\Cache($storage); } return $this->cache; } - public function getScanner($path = '') { + public function getScanner($path = '', $storage = null) { + if (!$storage) { + $storage = $this; + } if (!isset($this->scanner)) { - $this->scanner = new \OC\Files\Cache\Scanner($this); + $this->scanner = new \OC\Files\Cache\Scanner($storage); } return $this->scanner; } - public function getWatcher($path = '') { + public function getWatcher($path = '', $storage = null) { + if (!$storage) { + $storage = $this; + } if (!isset($this->watcher)) { - $this->watcher = new \OC\Files\Cache\Watcher($this); + $this->watcher = new \OC\Files\Cache\Watcher($storage); $this->watcher->setPolicy(\OC::$server->getConfig()->getSystemValue('filesystem_check_changes', Watcher::CHECK_ONCE)); } return $this->watcher; } - public function getStorageCache() { + public function getStorageCache($storage = null) { + if (!$storage) { + $storage = $this; + } if (!isset($this->storageCache)) { - $this->storageCache = new \OC\Files\Cache\Storage($this); + $this->storageCache = new \OC\Files\Cache\Storage($storage); } return $this->storageCache; } diff --git a/lib/private/files/storage/home.php b/lib/private/files/storage/home.php index f66096f6d9..214deede62 100644 --- a/lib/private/files/storage/home.php +++ b/lib/private/files/storage/home.php @@ -49,9 +49,12 @@ class Home extends Local { /** * @return \OC\Files\Cache\HomeCache */ - public function getCache($path = '') { + public function getCache($path = '', $storage = null) { + if (!$storage) { + $storage = $this; + } if (!isset($this->cache)) { - $this->cache = new \OC\Files\Cache\HomeCache($this); + $this->cache = new \OC\Files\Cache\HomeCache($storage); } return $this->cache; } diff --git a/lib/private/files/storage/storage.php b/lib/private/files/storage/storage.php index f085a0590b..2139f46482 100644 --- a/lib/private/files/storage/storage.php +++ b/lib/private/files/storage/storage.php @@ -19,17 +19,19 @@ interface Storage extends \OCP\Files\Storage { * get a cache instance for the storage * * @param string $path + * @param \OC\Files\Storage\Storage (optional) the storage to pass to the cache * @return \OC\Files\Cache\Cache */ - public function getCache($path = ''); + public function getCache($path = '', $storage = null); /** * get a scanner instance for the storage * * @param string $path + * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner * @return \OC\Files\Cache\Scanner */ - public function getScanner($path = ''); + public function getScanner($path = '', $storage = null); /** @@ -44,9 +46,10 @@ interface Storage extends \OCP\Files\Storage { * get a watcher instance for the cache * * @param string $path + * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher * @return \OC\Files\Cache\Watcher */ - public function getWatcher($path = ''); + public function getWatcher($path = '', $storage = null); /** * @return \OC\Files\Cache\Storage diff --git a/lib/private/files/storage/wrapper/wrapper.php b/lib/private/files/storage/wrapper/wrapper.php index 057c31c3cd..d899c88363 100644 --- a/lib/private/files/storage/wrapper/wrapper.php +++ b/lib/private/files/storage/wrapper/wrapper.php @@ -361,20 +361,28 @@ class Wrapper implements \OC\Files\Storage\Storage { * get a cache instance for the storage * * @param string $path + * @param \OC\Files\Storage\Storage (optional) the storage to pass to the cache * @return \OC\Files\Cache\Cache */ - public function getCache($path = '') { - return $this->storage->getCache($path); + public function getCache($path = '', $storage = null) { + if (!$storage) { + $storage = $this; + } + return $this->storage->getCache($path, $storage); } /** * get a scanner instance for the storage * * @param string $path + * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner * @return \OC\Files\Cache\Scanner */ - public function getScanner($path = '') { - return $this->storage->getScanner($path); + public function getScanner($path = '', $storage = null) { + if (!$storage) { + $storage = $this; + } + return $this->storage->getScanner($path, $storage); } @@ -392,10 +400,14 @@ class Wrapper implements \OC\Files\Storage\Storage { * get a watcher instance for the cache * * @param string $path + * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher * @return \OC\Files\Cache\Watcher */ - public function getWatcher($path = '') { - return $this->storage->getWatcher($path); + public function getWatcher($path = '', $storage = null) { + if (!$storage) { + $storage = $this; + } + return $this->storage->getWatcher($path, $storage); } /** @@ -417,6 +429,7 @@ class Wrapper implements \OC\Files\Storage\Storage { /** * Returns true + * * @return true */ public function test() { @@ -425,6 +438,7 @@ class Wrapper implements \OC\Files\Storage\Storage { /** * Returns the wrapped storage's value for isLocal() + * * @return bool wrapped storage's isLocal() value */ public function isLocal() { diff --git a/lib/private/files/view.php b/lib/private/files/view.php index afccdf9f73..91d7ea260d 100644 --- a/lib/private/files/view.php +++ b/lib/private/files/view.php @@ -161,7 +161,34 @@ class View { return $this->basicOperation('mkdir', $path, array('create', 'write')); } + protected function removeMount($mount, $path){ + if ($mount instanceof MoveableMount) { + \OC_Hook::emit( + Filesystem::CLASSNAME, "umount", + array(Filesystem::signal_param_path => $path) + ); + $result = $mount->removeMount(); + if ($result) { + \OC_Hook::emit( + Filesystem::CLASSNAME, "post_umount", + array(Filesystem::signal_param_path => $path) + ); + } + return $result; + } else { + // do not allow deleting the storage's root / the mount point + // because for some storages it might delete the whole contents + // but isn't supposed to work that way + return false; + } + } + public function rmdir($path) { + $absolutePath= $this->getAbsolutePath($path); + $mount = Filesystem::getMountManager()->find($absolutePath); + if ($mount->getInternalPath($absolutePath) === '') { + return $this->removeMount($mount, $path); + } if ($this->is_dir($path)) { return $this->basicOperation('rmdir', $path, array('delete')); } else { @@ -360,25 +387,7 @@ class View { $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); $mount = Filesystem::getMountManager()->find($absolutePath . $postFix); if ($mount->getInternalPath($absolutePath) === '') { - if ($mount instanceof MoveableMount) { - \OC_Hook::emit( - Filesystem::CLASSNAME, "umount", - array(Filesystem::signal_param_path => $path) - ); - $result = $mount->removeMount(); - if ($result) { - \OC_Hook::emit( - Filesystem::CLASSNAME, "post_umount", - array(Filesystem::signal_param_path => $path) - ); - } - return $result; - } else { - // do not allow deleting the storage's root / the mount point - // because for some storages it might delete the whole contents - // but isn't supposed to work that way - return false; - } + return $this->removeMount($mount, $path); } return $this->basicOperation('unlink', $path, array('delete')); } @@ -836,11 +845,10 @@ class View { return $data; } $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path); - /** - * @var \OC\Files\Storage\Storage $storage - * @var string $internalPath - */ - list($storage, $internalPath) = Filesystem::resolvePath($path); + + $mount = Filesystem::getMountManager()->find($path); + $storage = $mount->getStorage(); + $internalPath = $mount->getInternalPath($path); $data = null; if ($storage) { $cache = $storage->getCache($internalPath); @@ -888,6 +896,10 @@ class View { return false; } + if ($mount instanceof MoveableMount) { + $data['permissions'] |= \OCP\PERMISSION_DELETE | \OCP\PERMISSION_UPDATE; + } + $data = \OC_FileProxy::runPostProxies('getFileInfo', $path, $data); return new FileInfo($path, $storage, $internalPath, $data); diff --git a/lib/private/helper.php b/lib/private/helper.php index 3e2c1db79d..243baa4694 100644 --- a/lib/private/helper.php +++ b/lib/private/helper.php @@ -940,7 +940,7 @@ class OC_Helper { // return storage info without adding mount points $includeExtStorage = \OC_Config::getValue('quota_include_external_storage', false); - if (is_null($rootInfo)) { + if (!$rootInfo) { $rootInfo = \OC\Files\Filesystem::getFileInfo($path, false); } $used = $rootInfo->getSize(); diff --git a/lib/private/share/share.php b/lib/private/share/share.php index a3de8ebc0e..26108a937c 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -508,9 +508,9 @@ class Share extends \OC\Share\Constants { if ($itemType === 'folder') { $path = '/' . $uidOwner . '/files' . \OC\Files\Filesystem::getPath($itemSource) . '/'; $mountManager = \OC\Files\Filesystem::getMountManager(); - $mounts = $mountManager->getAll(); - foreach ($mounts as $mountPoint => $mount) { - if ($mount->getStorage() instanceof \OC\Files\Storage\Shared && strpos($mountPoint, $path) === 0) { + $mounts = $mountManager->findIn($path); + foreach ($mounts as $mount) { + if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) { $message = 'Sharing "' . $itemSourceName . '" failed, because it contains files shared with you!'; \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); throw new \Exception($message); diff --git a/tests/lib/connector/sabre/objecttree.php b/tests/lib/connector/sabre/objecttree.php index 0075b7832b..a88e23bbe2 100644 --- a/tests/lib/connector/sabre/objecttree.php +++ b/tests/lib/connector/sabre/objecttree.php @@ -110,7 +110,8 @@ class ObjectTree extends PHPUnit_Framework_TestCase { ->will($this->returnValue(false)); /** @var $objectTree \OC\Connector\Sabre\ObjectTree */ - $objectTree->init($rootDir, $view); + $mountManager = \OC\Files\Filesystem::getMountManager(); + $objectTree->init($rootDir, $view, $mountManager); $objectTree->move($source, $dest); }