diff options
Diffstat (limited to 'lib')
25 files changed, 628 insertions, 154 deletions
diff --git a/lib/autoloader.php b/lib/autoloader.php index 41a040b3f54..8361f31b038 100644 --- a/lib/autoloader.php +++ b/lib/autoloader.php @@ -51,7 +51,9 @@ class Autoloader { * @param string[] $validRoots */ public function __construct(array $validRoots) { - $this->validRoots = $validRoots; + foreach ($validRoots as $root) { + $this->validRoots[$root] = true; + } } /** @@ -60,7 +62,8 @@ class Autoloader { * @param string $root */ public function addValidRoot($root) { - $this->validRoots[] = stream_resolve_include_path($root); + $root = stream_resolve_include_path($root); + $this->validRoots[$root] = true; } /** @@ -126,7 +129,7 @@ class Autoloader { } protected function isValidPath($fullPath) { - foreach ($this->validRoots as $root) { + foreach ($this->validRoots as $root => $true) { if (substr($fullPath, 0, strlen($root) + 1) === $root . '/') { return true; } diff --git a/lib/base.php b/lib/base.php index 5f8a7ed0f19..f5ba05ded74 100644 --- a/lib/base.php +++ b/lib/base.php @@ -528,7 +528,8 @@ class OC { OC::$SERVERROOT . '/settings', OC::$SERVERROOT . '/ocs', OC::$SERVERROOT . '/ocs-provider', - OC::$SERVERROOT . '/3rdparty' + OC::$SERVERROOT . '/3rdparty', + OC::$SERVERROOT . '/tests', ]); spl_autoload_register(array(self::$loader, 'load')); $loaderEnd = microtime(true); @@ -1116,14 +1117,6 @@ class OC { return true; } - /** - * Get the temporary dir to store uploaded data - * @return null|string Path to the temporary directory or null - */ - function get_temp_dir() { - return \OC::$server->getTempManager()->t_get_temp_dir(); - } - } diff --git a/lib/l10n/sk_SK.js b/lib/l10n/sk_SK.js index ca0beaf334a..49fabed436f 100644 --- a/lib/l10n/sk_SK.js +++ b/lib/l10n/sk_SK.js @@ -72,6 +72,7 @@ OC.L10N.register( "Set an admin username." : "Zadajte používateľské meno administrátora.", "Set an admin password." : "Zadajte heslo administrátora.", "Can't create or write into the data directory %s" : "Nemožno vytvoriť alebo zapisovať do priečinka dát %s", + "Invalid Federated Cloud ID" : "Neplatné združené Cloud ID", "%s shared »%s« with you" : "%s s vami zdieľa »%s«", "%s via %s" : "%s cez %s", "Sharing %s failed, because the backend does not allow shares from type %i" : "Zdieľanie %s zlyhalo, pretože backend nepodporuje typ zdieľania %i", diff --git a/lib/l10n/sk_SK.json b/lib/l10n/sk_SK.json index 65334f58988..4eea36cda2f 100644 --- a/lib/l10n/sk_SK.json +++ b/lib/l10n/sk_SK.json @@ -70,6 +70,7 @@ "Set an admin username." : "Zadajte používateľské meno administrátora.", "Set an admin password." : "Zadajte heslo administrátora.", "Can't create or write into the data directory %s" : "Nemožno vytvoriť alebo zapisovať do priečinka dát %s", + "Invalid Federated Cloud ID" : "Neplatné združené Cloud ID", "%s shared »%s« with you" : "%s s vami zdieľa »%s«", "%s via %s" : "%s cez %s", "Sharing %s failed, because the backend does not allow shares from type %i" : "Zdieľanie %s zlyhalo, pretože backend nepodporuje typ zdieľania %i", diff --git a/lib/l10n/tr.js b/lib/l10n/tr.js index 828b5a85569..93f2908a69f 100644 --- a/lib/l10n/tr.js +++ b/lib/l10n/tr.js @@ -91,6 +91,7 @@ OC.L10N.register( "Sharing %s failed, because the user %s does not exist" : "%s paylaşımı, %s kullanıcısı mevcut olmadığından başarısız oldu", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "%s paylaşımı, %s kullanıcısının %s üyeliklerinden birine sahip olmadığından başarısız oldu", "Sharing %s failed, because this item is already shared with %s" : "%s paylaşımı, %s ile zaten paylaşıldığından dolayı başarısız oldu", + "Sharing %s failed, because this item is already shared with user %s" : "%s paylaşımı başarısız oldu çünkü bu öge zaten %s kullanıcısı ile paylaşılıyor", "Sharing %s failed, because the group %s does not exist" : "%s paylaşımı, %s grubu mevcut olmadığından başarısız oldu", "Sharing %s failed, because %s is not a member of the group %s" : "%s paylaşımı, %s kullanıcısı %s grup üyesi olmadığından başarısız oldu", "You need to provide a password to create a public link, only protected links are allowed" : "Herkese açık bir bağlantı oluşturmak için bir parola belirtmeniz gerekiyor. Sadece korunmuş bağlantılara izin verilmektedir", diff --git a/lib/l10n/tr.json b/lib/l10n/tr.json index 13b63ee6f7d..bb3cd291e64 100644 --- a/lib/l10n/tr.json +++ b/lib/l10n/tr.json @@ -89,6 +89,7 @@ "Sharing %s failed, because the user %s does not exist" : "%s paylaşımı, %s kullanıcısı mevcut olmadığından başarısız oldu", "Sharing %s failed, because the user %s is not a member of any groups that %s is a member of" : "%s paylaşımı, %s kullanıcısının %s üyeliklerinden birine sahip olmadığından başarısız oldu", "Sharing %s failed, because this item is already shared with %s" : "%s paylaşımı, %s ile zaten paylaşıldığından dolayı başarısız oldu", + "Sharing %s failed, because this item is already shared with user %s" : "%s paylaşımı başarısız oldu çünkü bu öge zaten %s kullanıcısı ile paylaşılıyor", "Sharing %s failed, because the group %s does not exist" : "%s paylaşımı, %s grubu mevcut olmadığından başarısız oldu", "Sharing %s failed, because %s is not a member of the group %s" : "%s paylaşımı, %s kullanıcısı %s grup üyesi olmadığından başarısız oldu", "You need to provide a password to create a public link, only protected links are allowed" : "Herkese açık bir bağlantı oluşturmak için bir parola belirtmeniz gerekiyor. Sadece korunmuş bağlantılara izin verilmektedir", diff --git a/lib/private/app.php b/lib/private/app.php index 6eba215ee27..e44290369a8 100644 --- a/lib/private/app.php +++ b/lib/private/app.php @@ -101,6 +101,15 @@ class OC_App { } // Load the enabled apps here $apps = self::getEnabledApps(); + + // Add each apps' folder as allowed class path + foreach($apps as $app) { + $path = self::getAppPath($app); + if($path !== false) { + \OC::$loader->addValidRoot($path); + } + } + // prevent app.php from printing output ob_start(); foreach ($apps as $app) { @@ -122,7 +131,7 @@ class OC_App { */ public static function loadApp($app, $checkUpgrade = true) { self::$loadedApps[] = $app; - \OC::$loader->addValidRoot(self::getAppPath($app)); + \OC::$loader->addValidRoot(self::getAppPath($app)); // in case someone calls loadApp() directly if (is_file(self::getAppPath($app) . '/appinfo/app.php')) { \OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app); if ($checkUpgrade and self::shouldUpgrade($app)) { diff --git a/lib/private/connector/sabre/directory.php b/lib/private/connector/sabre/directory.php index 551176e4bd2..0261ab18047 100644 --- a/lib/private/connector/sabre/directory.php +++ b/lib/private/connector/sabre/directory.php @@ -30,6 +30,7 @@ namespace OC\Connector\Sabre; use OC\Connector\Sabre\Exception\InvalidPath; use OC\Connector\Sabre\Exception\FileLocked; +use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; use Sabre\DAV\Exception\Locked; @@ -110,6 +111,7 @@ class Directory extends \OC\Connector\Sabre\Node // using a dummy FileInfo is acceptable here since it will be refreshed after the put is complete $info = new \OC\Files\FileInfo($path, null, null, array(), null); $node = new \OC\Connector\Sabre\File($this->fileView, $info); + $node->acquireLock(ILockingProvider::LOCK_SHARED); return $node->put($data); } catch (\OCP\Files\StorageNotAvailableException $e) { throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage()); diff --git a/lib/private/connector/sabre/file.php b/lib/private/connector/sabre/file.php index b7d0c547f24..17659c96b07 100644 --- a/lib/private/connector/sabre/file.php +++ b/lib/private/connector/sabre/file.php @@ -35,6 +35,7 @@ namespace OC\Connector\Sabre; use OC\Connector\Sabre\Exception\EntityTooLarge; use OC\Connector\Sabre\Exception\FileLocked; use OC\Connector\Sabre\Exception\UnsupportedMediaType; +use OC\Files\Filesystem; use OCP\Encryption\Exceptions\GenericEncryptionException; use OCP\Files\EntityTooLargeException; use OCP\Files\InvalidContentException; @@ -114,12 +115,6 @@ class File extends Node implements IFile { $partFilePath = $this->path; } - try { - $this->fileView->lockFile($this->path, ILockingProvider::LOCK_SHARED); - } catch (LockedException $e) { - throw new FileLocked($e->getMessage(), $e->getCode(), $e); - } - // the part file and target file might be on a different storage in case of a single file storage (e.g. single file share) /** @var \OC\Files\Storage\Storage $partStorage */ list($partStorage, $internalPartPath) = $this->fileView->resolvePath($partFilePath); @@ -132,7 +127,7 @@ class File extends Node implements IFile { // because we have no clue about the cause we can only throw back a 500/Internal Server Error throw new Exception('Could not write file contents'); } - list($count, ) = \OC_Helper::streamCopy($data, $target); + list($count,) = \OC_Helper::streamCopy($data, $target); fclose($target); // if content length is sent by client: @@ -154,29 +149,14 @@ class File extends Node implements IFile { try { $view = \OC\Files\Filesystem::getView(); - $run = true; if ($view) { - $hookPath = $view->getRelativePath($this->fileView->getAbsolutePath($this->path)); - - if (!$exists) { - \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_create, array( - \OC\Files\Filesystem::signal_param_path => $hookPath, - \OC\Files\Filesystem::signal_param_run => &$run, - )); - } else { - \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_update, array( - \OC\Files\Filesystem::signal_param_path => $hookPath, - \OC\Files\Filesystem::signal_param_run => &$run, - )); - } - \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_write, array( - \OC\Files\Filesystem::signal_param_path => $hookPath, - \OC\Files\Filesystem::signal_param_run => &$run, - )); + $run = $this->emitPreHooks($exists); + } else { + $run = true; } try { - $this->fileView->changeLock($this->path, ILockingProvider::LOCK_EXCLUSIVE); + $this->changeLock(ILockingProvider::LOCK_EXCLUSIVE); } catch (LockedException $e) { if ($needsPartFile) { $partStorage->unlink($internalPartPath); @@ -202,7 +182,7 @@ class File extends Node implements IFile { } try { - $this->fileView->changeLock($this->path, ILockingProvider::LOCK_SHARED); + $this->changeLock(ILockingProvider::LOCK_SHARED); } catch (LockedException $e) { throw new FileLocked($e->getMessage(), $e->getCode(), $e); } @@ -211,18 +191,7 @@ class File extends Node implements IFile { $this->fileView->getUpdater()->update($this->path); if ($view) { - if (!$exists) { - \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_create, array( - \OC\Files\Filesystem::signal_param_path => $hookPath - )); - } else { - \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_update, array( - \OC\Files\Filesystem::signal_param_path => $hookPath - )); - } - \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_write, array( - \OC\Files\Filesystem::signal_param_path => $hookPath - )); + $this->emitPostHooks($exists); } // allow sync clients to send the mtime along in a header @@ -233,7 +202,6 @@ class File extends Node implements IFile { } } $this->refreshInfo(); - $this->fileView->unlockFile($this->path, ILockingProvider::LOCK_SHARED); } catch (StorageNotAvailableException $e) { throw new ServiceUnavailable("Failed to check file size: " . $e->getMessage()); } @@ -241,6 +209,50 @@ class File extends Node implements IFile { return '"' . $this->info->getEtag() . '"'; } + private function emitPreHooks($exists, $path = null) { + if (is_null($path)) { + $path = $this->path; + } + $hookPath = Filesystem::getView()->getRelativePath($this->fileView->getAbsolutePath($path)); + $run = true; + + if (!$exists) { + \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_create, array( + \OC\Files\Filesystem::signal_param_path => $hookPath, + \OC\Files\Filesystem::signal_param_run => &$run, + )); + } else { + \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_update, array( + \OC\Files\Filesystem::signal_param_path => $hookPath, + \OC\Files\Filesystem::signal_param_run => &$run, + )); + } + \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_write, array( + \OC\Files\Filesystem::signal_param_path => $hookPath, + \OC\Files\Filesystem::signal_param_run => &$run, + )); + return $run; + } + + private function emitPostHooks($exists, $path = null) { + if (is_null($path)) { + $path = $this->path; + } + $hookPath = Filesystem::getView()->getRelativePath($this->fileView->getAbsolutePath($path)); + if (!$exists) { + \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_create, array( + \OC\Files\Filesystem::signal_param_path => $hookPath + )); + } else { + \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_update, array( + \OC\Files\Filesystem::signal_param_path => $hookPath + )); + } + \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_write, array( + \OC\Files\Filesystem::signal_param_path => $hookPath + )); + } + /** * Returns the data * @@ -354,15 +366,30 @@ class File extends Node implements IFile { $needsPartFile = $this->needsPartFile($storage); $partFile = null; + $targetPath = $path . '/' . $info['name']; + /** @var \OC\Files\Storage\Storage $targetStorage */ + list($targetStorage, $targetInternalPath) = $this->fileView->resolvePath($targetPath); + + $exists = $this->fileView->file_exists($targetPath); + try { - $targetPath = $path . '/' . $info['name']; + $this->emitPreHooks($exists, $targetPath); + + $this->changeLock(ILockingProvider::LOCK_EXCLUSIVE); + if ($needsPartFile) { // we first assembly the target file as a part file $partFile = $path . '/' . $info['name'] . '.ocTransferId' . $info['transferid'] . '.part'; - $chunk_handler->file_assemble($partFile); + + + list($partStorage, $partInternalPath) = $this->fileView->resolvePath($partFile); + + + $chunk_handler->file_assemble($partStorage, $partInternalPath, $this->fileView->getAbsolutePath($targetPath)); // here is the final atomic rename - $renameOkay = $this->fileView->rename($partFile, $targetPath); + $renameOkay = $targetStorage->moveFromStorage($partStorage, $partInternalPath, $targetInternalPath); + $fileExists = $this->fileView->file_exists($targetPath); if ($renameOkay === false || $fileExists === false) { \OCP\Util::writeLog('webdav', '\OC\Files\Filesystem::rename() failed', \OCP\Util::ERROR); @@ -371,28 +398,36 @@ class File extends Node implements IFile { // set to null to avoid double-deletion when handling exception // stray part file $partFile = null; - $this->fileView->unlink($targetPath); + $targetStorage->unlink($targetInternalPath); } + $this->changeLock(ILockingProvider::LOCK_SHARED); throw new Exception('Could not rename part file assembled from chunks'); } } else { // assemble directly into the final file - $chunk_handler->file_assemble($targetPath); + $chunk_handler->file_assemble($targetStorage, $targetInternalPath, $this->fileView->getAbsolutePath($targetPath)); } // allow sync clients to send the mtime along in a header $request = \OC::$server->getRequest(); if (isset($request->server['HTTP_X_OC_MTIME'])) { - if ($this->fileView->touch($targetPath, $request->server['HTTP_X_OC_MTIME'])) { + if ($targetStorage->touch($targetInternalPath, $request->server['HTTP_X_OC_MTIME'])) { header('X-OC-MTime: accepted'); } } + $this->changeLock(ILockingProvider::LOCK_SHARED); + + // since we skipped the view we need to scan and emit the hooks ourselves + $this->fileView->getUpdater()->update($targetPath); + + $this->emitPostHooks($exists, $targetPath); + $info = $this->fileView->getFileInfo($targetPath); return $info->getEtag(); } catch (\Exception $e) { if ($partFile !== null) { - $this->fileView->unlink($partFile); + $targetStorage->unlink($targetInternalPath); } $this->convertToSabreException($e); } diff --git a/lib/private/connector/sabre/listenerplugin.php b/lib/private/connector/sabre/listenerplugin.php new file mode 100644 index 00000000000..d0d40f4dc86 --- /dev/null +++ b/lib/private/connector/sabre/listenerplugin.php @@ -0,0 +1,58 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @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\Connector\Sabre; + +use Sabre\DAV\ServerPlugin; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +class ListenerPlugin extends ServerPlugin { + /** @var EventDispatcherInterface */ + protected $dispatcher; + + /** + * @param EventDispatcherInterface $dispatcher + */ + public function __construct(EventDispatcherInterface $dispatcher) { + $this->dispatcher = $dispatcher; + } + + /** + * This initialize the plugin + * + * @param \Sabre\DAV\Server $server + */ + public function initialize(\Sabre\DAV\Server $server) { + $server->on('beforeMethod', array($this, 'emitListener'), 15); + } + + /** + * This method is called before any HTTP method and returns http status code 503 + * in case the system is in maintenance mode. + * + * @return bool + */ + public function emitListener() { + $this->dispatcher->dispatch('OC\Connector\Sabre::beforeMethod'); + + return true; + } +} diff --git a/lib/private/connector/sabre/lockplugin.php b/lib/private/connector/sabre/lockplugin.php new file mode 100644 index 00000000000..a3a7bb84e39 --- /dev/null +++ b/lib/private/connector/sabre/lockplugin.php @@ -0,0 +1,97 @@ +<?php +/** + * @author Robin Appelman <icewind@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @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\Connector\Sabre; + +use OC\Connector\Sabre\Exception\FileLocked; +use OCP\Lock\ILockingProvider; +use OCP\Lock\LockedException; +use Sabre\DAV\Exception\NotFound; +use \Sabre\DAV\PropFind; +use \Sabre\DAV\PropPatch; +use Sabre\DAV\ServerPlugin; +use Sabre\DAV\Tree; +use Sabre\HTTP\RequestInterface; +use Sabre\HTTP\ResponseInterface; + +class LockPlugin extends ServerPlugin { + /** + * Reference to main server object + * + * @var \Sabre\DAV\Server + */ + private $server; + + /** + * @var \Sabre\DAV\Tree + */ + private $tree; + + /** + * @param \Sabre\DAV\Tree $tree tree + */ + public function __construct(Tree $tree) { + $this->tree = $tree; + } + + /** + * {@inheritdoc} + */ + public function initialize(\Sabre\DAV\Server $server) { + $this->server = $server; + $this->server->on('beforeMethod', [$this, 'getLock'], 50); + $this->server->on('afterMethod', [$this, 'releaseLock'], 50); + } + + public function getLock(RequestInterface $request) { + // we cant listen on 'beforeMethod:PUT' due to order of operations with setting up the tree + // so instead we limit ourselves to the PUT method manually + if ($request->getMethod() !== 'PUT') { + return; + } + try { + $node = $this->tree->getNodeForPath($request->getPath()); + } catch (NotFound $e) { + return; + } + if ($node instanceof Node) { + try { + $node->acquireLock(ILockingProvider::LOCK_SHARED); + } catch (LockedException $e) { + throw new FileLocked($e->getMessage(), $e->getCode(), $e); + } + } + } + + public function releaseLock(RequestInterface $request) { + if ($request->getMethod() !== 'PUT') { + return; + } + try { + $node = $this->tree->getNodeForPath($request->getPath()); + } catch (NotFound $e) { + return; + } + if ($node instanceof Node) { + $node->releaseLock(ILockingProvider::LOCK_SHARED); + } + } +} diff --git a/lib/private/connector/sabre/maintenanceplugin.php b/lib/private/connector/sabre/maintenanceplugin.php index 5b48abbc517..f886332418a 100644 --- a/lib/private/connector/sabre/maintenanceplugin.php +++ b/lib/private/connector/sabre/maintenanceplugin.php @@ -65,7 +65,7 @@ class MaintenancePlugin extends ServerPlugin { */ public function initialize(\Sabre\DAV\Server $server) { $this->server = $server; - $this->server->on('beforeMethod', array($this, 'checkMaintenanceMode'), 10); + $this->server->on('beforeMethod', array($this, 'checkMaintenanceMode'), 1); } /** diff --git a/lib/private/connector/sabre/node.php b/lib/private/connector/sabre/node.php index 7f4bf8ffed1..30faf9941bd 100644 --- a/lib/private/connector/sabre/node.php +++ b/lib/private/connector/sabre/node.php @@ -67,6 +67,7 @@ abstract class Node implements \Sabre\DAV\INode { /** * Sets up the node, expects a full path name + * * @param \OC\Files\View $view * @param \OCP\Files\FileInfo $info */ @@ -82,6 +83,7 @@ abstract class Node implements \Sabre\DAV\INode { /** * Returns the name of the node + * * @return string */ public function getName() { @@ -99,6 +101,7 @@ abstract class Node implements \Sabre\DAV\INode { /** * Renames the node + * * @param string $name The new name * @throws \Sabre\DAV\Exception\BadRequest * @throws \Sabre\DAV\Exception\Forbidden @@ -131,6 +134,7 @@ abstract class Node implements \Sabre\DAV\INode { /** * Returns the last modification time, as a unix timestamp + * * @return int timestamp as integer */ public function getLastModified() { @@ -212,7 +216,7 @@ abstract class Node implements \Sabre\DAV\INode { * @return string|null */ public function getDavPermissions() { - $p =''; + $p = ''; if ($this->info->isShared()) { $p .= 'S'; } @@ -248,4 +252,25 @@ abstract class Node implements \Sabre\DAV\INode { throw new InvalidPath($ex->getMessage()); } } + + /** + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE + */ + public function acquireLock($type) { + $this->fileView->lockFile($this->path, $type); + } + + /** + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE + */ + public function releaseLock($type) { + $this->fileView->unlockFile($this->path, $type); + } + + /** + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE + */ + public function changeLock($type) { + $this->fileView->changeLock($this->path, $type); + } } diff --git a/lib/private/connector/sabre/serverfactory.php b/lib/private/connector/sabre/serverfactory.php index 525ff0104cd..54470b0b8aa 100644 --- a/lib/private/connector/sabre/serverfactory.php +++ b/lib/private/connector/sabre/serverfactory.php @@ -28,6 +28,7 @@ use OCP\ILogger; use OCP\ITagManager; use OCP\IUserSession; use Sabre\DAV\Auth\Backend\BackendInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; class ServerFactory { public function __construct( @@ -36,7 +37,8 @@ class ServerFactory { IDBConnection $databaseConnection, IUserSession $userSession, IMountManager $mountManager, - ITagManager $tagManager + ITagManager $tagManager, + EventDispatcherInterface $dispatcher ) { $this->config = $config; $this->logger = $logger; @@ -44,6 +46,7 @@ class ServerFactory { $this->userSession = $userSession; $this->mountManager = $mountManager; $this->tagManager = $tagManager; + $this->dispatcher = $dispatcher; } /** @@ -63,13 +66,15 @@ class ServerFactory { // Load plugins $defaults = new \OC_Defaults(); + $server->addPlugin(new \OC\Connector\Sabre\MaintenancePlugin($this->config)); $server->addPlugin(new \OC\Connector\Sabre\BlockLegacyClientPlugin($this->config)); $server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, $defaults->getName())); // FIXME: The following line is a workaround for legacy components relying on being able to send a GET to / $server->addPlugin(new \OC\Connector\Sabre\DummyGetResponsePlugin()); $server->addPlugin(new \OC\Connector\Sabre\FilesPlugin($objectTree)); - $server->addPlugin(new \OC\Connector\Sabre\MaintenancePlugin($this->config)); $server->addPlugin(new \OC\Connector\Sabre\ExceptionLoggerPlugin('webdav', $this->logger)); + $server->addPlugin(new \OC\Connector\Sabre\LockPlugin($objectTree)); + $server->addPlugin(new \OC\Connector\Sabre\ListenerPlugin($this->dispatcher)); // wait with registering these until auth is handled and the filesystem is setup $server->on('beforeMethod', function () use ($server, $objectTree, $viewCallBack) { diff --git a/lib/private/console/application.php b/lib/private/console/application.php index 7c709927219..edfb45c8577 100644 --- a/lib/private/console/application.php +++ b/lib/private/console/application.php @@ -55,7 +55,9 @@ class Application { if (!\OCP\Util::needUpgrade()) { OC_App::loadApps(); foreach (\OC::$server->getAppManager()->getInstalledApps() as $app) { - $file = OC_App::getAppPath($app) . '/appinfo/register_command.php'; + $appPath = \OC_App::getAppPath($app); + \OC::$loader->addValidRoot($appPath); + $file = $appPath . '/appinfo/register_command.php'; if (file_exists($file)) { require $file; } diff --git a/lib/private/encryption/decryptall.php b/lib/private/encryption/decryptall.php new file mode 100644 index 00000000000..e59be17886d --- /dev/null +++ b/lib/private/encryption/decryptall.php @@ -0,0 +1,268 @@ +<?php +/** + * @author Björn Schießle <schiessle@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @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\Encryption; + +use OC\Encryption\Exceptions\DecryptionFailedException; +use OC\Files\View; +use \OCP\Encryption\IEncryptionModule; +use OCP\IUserManager; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class DecryptAll { + + /** @var OutputInterface */ + protected $output; + + /** @var InputInterface */ + protected $input; + + /** @var Manager */ + protected $encryptionManager; + + /** @var IUserManager */ + protected $userManager; + + /** @var View */ + protected $rootView; + + /** @var array files which couldn't be decrypted */ + protected $failed; + + /** + * @param Manager $encryptionManager + * @param IUserManager $userManager + * @param View $rootView + */ + public function __construct( + Manager $encryptionManager, + IUserManager $userManager, + View $rootView + ) { + $this->encryptionManager = $encryptionManager; + $this->userManager = $userManager; + $this->rootView = $rootView; + $this->failed = []; + } + + /** + * start to decrypt all files + * + * @param InputInterface $input + * @param OutputInterface $output + * @param string $user which users data folder should be decrypted, default = all users + * @return bool + * @throws \Exception + */ + public function decryptAll(InputInterface $input, OutputInterface $output, $user = '') { + + $this->input = $input; + $this->output = $output; + + $this->output->writeln('prepare encryption modules...'); + if ($this->prepareEncryptionModules($user) === false) { + return false; + } + $this->output->writeln(' done.'); + + $this->decryptAllUsersFiles($user); + + if (empty($this->failed)) { + $this->output->writeln('all files could be decrypted successfully!'); + } else { + $this->output->writeln('Files for following users couldn\'t be decrypted, '); + $this->output->writeln('maybe the user is not set up in a way that supports this operation: '); + foreach ($this->failed as $uid => $paths) { + $this->output->writeln(' ' . $uid); + } + $this->output->writeln(''); + } + + return true; + } + + /** + * prepare encryption modules to perform the decrypt all function + * + * @param $user + * @return bool + */ + protected function prepareEncryptionModules($user) { + // prepare all encryption modules for decrypt all + $encryptionModules = $this->encryptionManager->getEncryptionModules(); + foreach ($encryptionModules as $moduleDesc) { + /** @var IEncryptionModule $module */ + $module = call_user_func($moduleDesc['callback']); + if ($module->prepareDecryptAll($this->input, $this->output, $user) === false) { + $this->output->writeln('Module "' . $moduleDesc['displayName'] . '" does not support the functionality to decrypt all files again or the initialization of the module failed!'); + return false; + } + } + + return true; + } + + /** + * iterate over all user and encrypt their files + * @param string $user which users files should be decrypted, default = all users + */ + protected function decryptAllUsersFiles($user = '') { + + $this->output->writeln("\n"); + + $userList = []; + if (empty($user)) { + + $fetchUsersProgress = new ProgressBar($this->output); + $fetchUsersProgress->setFormat(" %message% \n [%bar%]"); + $fetchUsersProgress->start(); + $fetchUsersProgress->setMessage("Fetch list of users..."); + $fetchUsersProgress->advance(); + + foreach ($this->userManager->getBackends() as $backend) { + $limit = 500; + $offset = 0; + do { + $users = $backend->getUsers('', $limit, $offset); + foreach ($users as $user) { + $userList[] = $user; + } + $offset += $limit; + $fetchUsersProgress->advance(); + } while (count($users) >= $limit); + $fetchUsersProgress->setMessage("Fetch list of users... finished"); + $fetchUsersProgress->finish(); + } + } else { + $userList[] = $user; + } + + $this->output->writeln("\n\n"); + + $progress = new ProgressBar($this->output); + $progress->setFormat(" %message% \n [%bar%]"); + $progress->start(); + $progress->setMessage("starting to decrypt files..."); + $progress->advance(); + + $numberOfUsers = count($userList); + $userNo = 1; + foreach ($userList as $uid) { + $userCount = "$uid ($userNo of $numberOfUsers)"; + $this->decryptUsersFiles($uid, $progress, $userCount); + $userNo++; + } + + $progress->setMessage("starting to decrypt files... finished"); + $progress->finish(); + + $this->output->writeln("\n\n"); + + } + + /** + * encrypt files from the given user + * + * @param string $uid + * @param ProgressBar $progress + * @param string $userCount + */ + protected function decryptUsersFiles($uid, ProgressBar $progress, $userCount) { + + $this->setupUserFS($uid); + $directories = array(); + $directories[] = '/' . $uid . '/files'; + + while($root = array_pop($directories)) { + $content = $this->rootView->getDirectoryContent($root); + foreach ($content as $file) { + $path = $root . '/' . $file['name']; + if ($this->rootView->is_dir($path)) { + $directories[] = $path; + continue; + } else { + try { + $progress->setMessage("decrypt files for user $userCount: $path"); + $progress->advance(); + if ($this->decryptFile($path) === false) { + $progress->setMessage("decrypt files for user $userCount: $path (already decrypted)"); + $progress->advance(); + } + } catch (\Exception $e) { + if (isset($this->failed[$uid])) { + $this->failed[$uid][] = $path; + } else { + $this->failed[$uid] = [$path]; + } + } + } + } + } + } + + /** + * encrypt file + * + * @param string $path + * @return bool + */ + protected function decryptFile($path) { + + $source = $path; + $target = $path . '.decrypted.' . $this->getTimestamp(); + + try { + $this->rootView->copy($source, $target); + $this->rootView->rename($target, $source); + } catch (DecryptionFailedException $e) { + if ($this->rootView->file_exists($target)) { + $this->rootView->unlink($target); + } + return false; + } + + return true; + } + + /** + * get current timestamp + * + * @return int + */ + protected function getTimestamp() { + return time(); + } + + + /** + * setup user file system + * + * @param string $uid + */ + protected function setupUserFS($uid) { + \OC_Util::tearDownFS(); + \OC_Util::setupFS($uid); + } + +} diff --git a/lib/private/filechunking.php b/lib/private/filechunking.php index 82bf61fa7b1..64399ad4366 100644 --- a/lib/private/filechunking.php +++ b/lib/private/filechunking.php @@ -178,27 +178,26 @@ class OC_FileChunking { * Assembles the chunks into the file specified by the path. * Also triggers the relevant hooks and proxies. * - * @param string $path target path + * @param \OC\Files\Storage\Storage $storage + * @param string $path target path relative to the storage + * @param string $absolutePath + * @return bool assembled file size or false if file could not be created * - * @return boolean assembled file size or false if file could not be created - * - * @throws \OC\InsufficientStorageException when file could not be fully - * assembled due to lack of free space + * @throws \OC\ServerNotAvailableException */ - public function file_assemble($path) { - $absolutePath = \OC\Files\Filesystem::normalizePath(\OC\Files\Filesystem::getView()->getAbsolutePath($path)); + public function file_assemble($storage, $path, $absolutePath) { $data = ''; // use file_put_contents as method because that best matches what this function does if (\OC\Files\Filesystem::isValidPath($path)) { - $path = \OC\Files\Filesystem::getView()->getRelativePath($absolutePath); - $exists = \OC\Files\Filesystem::file_exists($path); + $exists = $storage->file_exists($path); $run = true; + $hookPath = \OC\Files\Filesystem::getView()->getRelativePath($absolutePath); if(!$exists) { OC_Hook::emit( \OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_create, array( - \OC\Files\Filesystem::signal_param_path => $path, + \OC\Files\Filesystem::signal_param_path => $hookPath, \OC\Files\Filesystem::signal_param_run => &$run ) ); @@ -207,14 +206,14 @@ class OC_FileChunking { \OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_write, array( - \OC\Files\Filesystem::signal_param_path => $path, + \OC\Files\Filesystem::signal_param_path => $hookPath, \OC\Files\Filesystem::signal_param_run => &$run ) ); if(!$run) { return false; } - $target = \OC\Files\Filesystem::fopen($path, 'w'); + $target = $storage->fopen($path, 'w'); if($target) { $count = $this->assemble($target); fclose($target); @@ -222,13 +221,13 @@ class OC_FileChunking { OC_Hook::emit( \OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_create, - array( \OC\Files\Filesystem::signal_param_path => $path) + array( \OC\Files\Filesystem::signal_param_path => $hookPath) ); } OC_Hook::emit( \OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_write, - array( \OC\Files\Filesystem::signal_param_path => $path) + array( \OC\Files\Filesystem::signal_param_path => $hookPath) ); return $count > 0; }else{ diff --git a/lib/private/files/cache/storage.php b/lib/private/files/cache/storage.php index 338d8308281..88ceb287fb9 100644 --- a/lib/private/files/cache/storage.php +++ b/lib/private/files/cache/storage.php @@ -58,7 +58,8 @@ class Storage { $this->numericId = $row['numeric_id']; } else { $connection = \OC_DB::getConnection(); - if ($connection->insertIfNotExist('*PREFIX*storages', ['id' => $this->storageId, 'available' => $isAvailable])) { + $available = $isAvailable ? 1 : 0; + if ($connection->insertIfNotExist('*PREFIX*storages', ['id' => $this->storageId, 'available' => $available])) { $this->numericId = \OC_DB::insertid('*PREFIX*storages'); } else { if ($row = self::getStorageById($this->storageId)) { @@ -141,7 +142,7 @@ class Storage { public function getAvailability() { if ($row = self::getStorageById($this->storageId)) { return [ - 'available' => $row['available'], + 'available' => ((int)$row['available'] === 1), 'last_checked' => $row['last_checked'] ]; } else { @@ -154,7 +155,8 @@ class Storage { */ public function setAvailability($isAvailable) { $sql = 'UPDATE `*PREFIX*storages` SET `available` = ?, `last_checked` = ? WHERE `id` = ?'; - \OC_DB::executeAudited($sql, array($isAvailable, time(), $this->storageId)); + $available = $isAvailable ? 1 : 0; + \OC_DB::executeAudited($sql, array($available, time(), $this->storageId)); } /** diff --git a/lib/private/files/node/folder.php b/lib/private/files/node/folder.php index 042b515fea1..23004fc3527 100644 --- a/lib/private/files/node/folder.php +++ b/lib/private/files/node/folder.php @@ -25,7 +25,7 @@ namespace OC\Files\Node; -use OC\Files\Cache\Cache; +use OCP\Files\FileInfo; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; @@ -77,76 +77,15 @@ class Folder extends Node implements \OCP\Files\Folder { * @return Node[] */ public function getDirectoryListing() { - $result = array(); - - /** - * @var \OC\Files\Storage\Storage $storage - */ - list($storage, $internalPath) = $this->view->resolvePath($this->path); - if ($storage) { - $cache = $storage->getCache($internalPath); - - //trigger cache update check - $this->view->getFileInfo($this->path); - - $files = $cache->getFolderContents($internalPath); - } else { - $files = array(); - } - - //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders - $mounts = $this->root->getMountsIn($this->path); - $dirLength = strlen($this->path); - foreach ($mounts as $mount) { - $subStorage = $mount->getStorage(); - if ($subStorage) { - $subCache = $subStorage->getCache(''); + $folderContent = $this->view->getDirectoryContent($this->path); - if ($subCache->getStatus('') === Cache::NOT_FOUND) { - $subScanner = $subStorage->getScanner(''); - $subScanner->scanFile(''); - } - - $rootEntry = $subCache->get(''); - if ($rootEntry) { - $relativePath = trim(substr($mount->getMountPoint(), $dirLength), '/'); - if ($pos = strpos($relativePath, '/')) { - //mountpoint inside subfolder add size to the correct folder - $entryName = substr($relativePath, 0, $pos); - foreach ($files as &$entry) { - if ($entry['name'] === $entryName) { - if ($rootEntry['size'] >= 0) { - $entry['size'] += $rootEntry['size']; - } else { - $entry['size'] = -1; - } - } - } - } else { //mountpoint in this folder, add an entry for it - $rootEntry['name'] = $relativePath; - $rootEntry['storageObject'] = $subStorage; - - //remove any existing entry with the same name - foreach ($files as $i => $file) { - if ($file['name'] === $rootEntry['name']) { - $files[$i] = null; - break; - } - } - $files[] = $rootEntry; - } - } - } - } - - foreach ($files as $file) { - if ($file) { - $node = $this->createNode($this->path . '/' . $file['name'], $file); - $result[] = $node; + return array_map(function(FileInfo $info) { + if ($info->getMimetype() === 'httpd/unix-directory') { + return new Folder($this->root, $this->view, $info->getPath(), $info); + } else { + return new File($this->root, $this->view, $info->getPath(), $info); } - } - - return $result; + }, $folderContent); } /** diff --git a/lib/private/files/node/node.php b/lib/private/files/node/node.php index b9bd785de5c..943d12122e6 100644 --- a/lib/private/files/node/node.php +++ b/lib/private/files/node/node.php @@ -56,11 +56,13 @@ class Node implements \OCP\Files\Node { * @param \OC\Files\View $view * @param \OC\Files\Node\Root $root * @param string $path + * @param FileInfo $fileInfo */ - public function __construct($root, $view, $path) { + public function __construct($root, $view, $path, $fileInfo = null) { $this->view = $view; $this->root = $root; $this->path = $path; + $this->fileInfo = $fileInfo; } /** diff --git a/lib/private/notification/imanager.php b/lib/private/notification/imanager.php index 0cd92b33251..f4a5fb14e31 100644 --- a/lib/private/notification/imanager.php +++ b/lib/private/notification/imanager.php @@ -53,4 +53,10 @@ interface IManager extends IApp, INotifier { * @since 8.2.0 */ public function createNotification(); + + /** + * @return bool + * @since 8.2.0 + */ + public function hasNotifiers(); } diff --git a/lib/private/notification/manager.php b/lib/private/notification/manager.php index 9635925e38e..0d5bb9be514 100644 --- a/lib/private/notification/manager.php +++ b/lib/private/notification/manager.php @@ -113,6 +113,14 @@ class Manager implements IManager { } /** + * @return bool + * @since 8.2.0 + */ + public function hasNotifiers() { + return !empty($this->notifiersClosures); + } + + /** * @param INotification $notification * @return null * @throws \InvalidArgumentException When the notification is not valid diff --git a/lib/private/preview/office.php b/lib/private/preview/office.php index 30398147aa6..ac6b4afa0cc 100644 --- a/lib/private/preview/office.php +++ b/lib/private/preview/office.php @@ -36,7 +36,7 @@ abstract class Office extends Provider { $absPath = $fileview->toTmpFile($path); - $tmpDir = get_temp_dir(); + $tmpDir = \OC::$server->getTempManager()->getTempBaseDir(); $defaultParameters = ' -env:UserInstallation=file://' . escapeshellarg($tmpDir . '/owncloud-' . \OC_Util::getInstanceId() . '/') . ' --headless --nologo --nofirststartwizard --invisible --norestore --convert-to pdf --outdir '; $clParameters = \OCP\Config::getSystemValue('preview_office_cl_parameters', $defaultParameters); diff --git a/lib/private/share/share.php b/lib/private/share/share.php index 6ad36d60fe8..802b146cfb6 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -83,6 +83,13 @@ class Share extends Constants { 'supportedFileExtensions' => $supportedFileExtensions ); if(count(self::$backendTypes) === 1) { + \OC_Util::addScript('core', 'shareconfigmodel'); + \OC_Util::addScript('core', 'shareitemmodel'); + \OC_Util::addScript('core', 'sharedialogresharerinfoview'); + \OC_Util::addScript('core', 'sharedialoglinkshareview'); + \OC_Util::addScript('core', 'sharedialogexpirationview'); + \OC_Util::addScript('core', 'sharedialogshareelistview'); + \OC_Util::addScript('core', 'sharedialogview'); \OC_Util::addScript('core', 'share'); \OC_Util::addStyle('core', 'share'); } diff --git a/lib/public/encryption/iencryptionmodule.php b/lib/public/encryption/iencryptionmodule.php index a5cd7075691..66cf1a80e0d 100644 --- a/lib/public/encryption/iencryptionmodule.php +++ b/lib/public/encryption/iencryptionmodule.php @@ -141,9 +141,19 @@ interface IEncryptionModule { * * @param InputInterface $input * @param OutputInterface $output write some status information to the terminal during encryption - * @return bool * @since 8.2.0 */ public function encryptAll(InputInterface $input, OutputInterface $output); + /** + * prepare encryption module to decrypt all files + * + * @param InputInterface $input + * @param OutputInterface $output write some status information to the terminal during encryption + * @param $user (optional) for which the files should be decrypted, default = all users + * @return bool return false on failure or if it isn't supported by the module + * @since 8.2.0 + */ + public function prepareDecryptAll(InputInterface $input, OutputInterface $output, $user = ''); + } |