diff options
author | Robin Appelman <icewind@owncloud.com> | 2014-04-25 11:47:06 +0200 |
---|---|---|
committer | Robin Appelman <icewind@owncloud.com> | 2014-04-25 11:47:06 +0200 |
commit | 6c20a014eaecd19c3f68143485c6f74891ee9643 (patch) | |
tree | 84bd8e37536e7f28a25afd7586c209d38a25d610 /apps/files_sharing/lib | |
parent | cd0c5990f895bcdce47acf2dbf11ebadd920a404 (diff) | |
parent | 3fc809dfd80a296d7da922a06f9e13d446b3d3f0 (diff) | |
download | nextcloud-server-6c20a014eaecd19c3f68143485c6f74891ee9643.tar.gz nextcloud-server-6c20a014eaecd19c3f68143485c6f74891ee9643.zip |
merge master into webdav-injection
Diffstat (limited to 'apps/files_sharing/lib')
-rw-r--r-- | apps/files_sharing/lib/api.php | 12 | ||||
-rw-r--r-- | apps/files_sharing/lib/cache.php | 110 | ||||
-rw-r--r-- | apps/files_sharing/lib/helper.php | 73 | ||||
-rw-r--r-- | apps/files_sharing/lib/permissions.php | 4 | ||||
-rw-r--r-- | apps/files_sharing/lib/share/file.php | 141 | ||||
-rw-r--r-- | apps/files_sharing/lib/sharedstorage.php | 294 | ||||
-rw-r--r-- | apps/files_sharing/lib/updater.php | 77 | ||||
-rw-r--r-- | apps/files_sharing/lib/watcher.php | 2 |
8 files changed, 462 insertions, 251 deletions
diff --git a/apps/files_sharing/lib/api.php b/apps/files_sharing/lib/api.php index de3c1cd2630..438d3cc4ba3 100644 --- a/apps/files_sharing/lib/api.php +++ b/apps/files_sharing/lib/api.php @@ -184,7 +184,6 @@ class Api { $receivedFrom = \OCP\Share::getItemSharedWithBySource($itemType, $file['fileid']); reset($share); $key = key($share); - $share[$key]['path'] = self::correctPath($share[$key]['path'], $path); if ($receivedFrom) { $share[$key]['received_from'] = $receivedFrom['uid_owner']; $share[$key]['received_from_displayname'] = \OCP\User::getDisplayName($receivedFrom['uid_owner']); @@ -531,15 +530,4 @@ class Api { } - /** - * @brief make sure that the path has the correct root - * - * @param string $path path returned from the share API - * @param string $folder current root folder - * @return string the correct path - */ - protected static function correctPath($path, $folder) { - return \OC_Filesystem::normalizePath('/' . $folder . '/' . basename($path)); - } - } diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index eeb62c3cce2..67a0410ef76 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -2,8 +2,9 @@ /** * ownCloud * - * @author Michael Gapczynski - * @copyright 2012 Michael Gapczynski mtgap@owncloud.com + * @author Bjoern Schiessle, Michael Gapczynski + * @copyright 2012 Michael Gapczynski <mtgap@owncloud.com> + * 2014 Bjoern Schiessle <schiessle@owncloud.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -46,12 +47,15 @@ class Shared_Cache extends Cache { * @return \OC\Files\Cache\Cache */ private function getSourceCache($target) { - $source = \OC_Share_Backend_File::getSource($target); + if ($target === false || $target === $this->storage->getMountPoint()) { + $target = ''; + } + $source = \OC_Share_Backend_File::getSource($target, $this->storage->getMountPoint(), $this->storage->getItemType()); if (isset($source['path']) && isset($source['fileOwner'])) { \OC\Files\Filesystem::initMountPoints($source['fileOwner']); - $mount = \OC\Files\Filesystem::getMountByNumericId($source['storage']); - if (is_array($mount)) { - $fullPath = $mount[key($mount)]->getMountPoint() . $source['path']; + $mounts = \OC\Files\Filesystem::getMountByNumericId($source['storage']); + if (is_array($mounts) and count($mounts)) { + $fullPath = $mounts[0]->getMountPoint() . $source['path']; list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($fullPath); if ($storage) { $this->files[$target] = $internalPath; @@ -80,23 +84,28 @@ class Shared_Cache extends Cache { * @return array */ public function get($file) { - if ($file == '') { - $data = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_FILE_APP_ROOT); - $etag = \OCP\Config::getUserValue(\OCP\User::getUser(), 'files_sharing', 'etag'); - if (!isset($etag)) { - $etag = $this->storage->getETag(''); - \OCP\Config::setUserValue(\OCP\User::getUser(), 'files_sharing', 'etag', $etag); - } - $data['etag'] = $etag; - return $data; - } else if (is_string($file)) { + if (is_string($file)) { if ($cache = $this->getSourceCache($file)) { - return $cache->get($this->files[$file]); + $path = 'files' . $this->storage->getMountPoint(); + $path .= ($file !== '') ? '/' . $file : ''; + $data = $cache->get($this->files[$file]); + $data['displayname_owner'] = \OC_User::getDisplayName($this->storage->getSharedFrom()); + $data['path'] = $path; + if ($file === '') { + $data['is_share_mount_point'] = true; + } + return $data; } } else { + // if we are at the root of the mount point we want to return the + // cache information for the source item + if (!is_int($file) || $file === 0) { + $file = $this->storage->getSourceId(); + $mountPoint = $this->storage->getMountPoint(); + } $query = \OC_DB::prepare( 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`,' - . ' `size`, `mtime`, `encrypted`, `unencrypted_size`' + . ' `size`, `mtime`, `encrypted`, `unencrypted_size`, `storage_mtime`, `etag`' . ' FROM `*PREFIX*filecache` WHERE `fileid` = ?'); $result = $query->execute(array($file)); $data = $result->fetchRow(); @@ -115,6 +124,10 @@ class Shared_Cache extends Cache { } else { $data['size'] = (int)$data['size']; } + if (isset($mountPoint)) { + $data['path'] = 'files/' . $mountPoint; + $data['is_share_mount_point'] = true; + } return $data; } return false; @@ -127,28 +140,26 @@ class Shared_Cache extends Cache { * @return array */ public function getFolderContents($folder) { - if ($folder == '') { - $files = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_FOLDER_CONTENTS); - foreach ($files as &$file) { - $file['mimetype'] = $this->getMimetype($file['mimetype']); - $file['mimepart'] = $this->getMimetype($file['mimepart']); - $file['usersPath'] = 'files/Shared/' . ltrim($file['path'], '/'); - } - return $files; - } else { - $cache = $this->getSourceCache($folder); - if ($cache) { - $parent = $this->storage->getFile($folder); - $sourceFolderContent = $cache->getFolderContents($this->files[$folder]); - foreach ($sourceFolderContent as $key => $c) { - $sourceFolderContent[$key]['usersPath'] = 'files/Shared/' . $folder . '/' . $c['name']; - $sourceFolderContent[$key]['uid_owner'] = $parent['uid_owner']; - $sourceFolderContent[$key]['displayname_owner'] = $parent['uid_owner']; - } - return $sourceFolderContent; + if ($folder === false) { + $folder = ''; + } + + $dir = ($folder !== '') ? $folder . '/' : ''; + + $cache = $this->getSourceCache($folder); + if ($cache) { + $parent = $this->storage->getFile($folder); + $sourceFolderContent = $cache->getFolderContents($this->files[$folder]); + foreach ($sourceFolderContent as $key => $c) { + $sourceFolderContent[$key]['path'] = $dir . $c['name']; + $sourceFolderContent[$key]['uid_owner'] = $parent['uid_owner']; + $sourceFolderContent[$key]['displayname_owner'] = $parent['uid_owner']; } + + return $sourceFolderContent; } + return false; } @@ -161,9 +172,8 @@ class Shared_Cache extends Cache { * @return int file id */ public function put($file, array $data) { - if ($file === '' && isset($data['etag'])) { - return \OCP\Config::setUserValue(\OCP\User::getUser(), 'files_sharing', 'etag', $data['etag']); - } else if ($cache = $this->getSourceCache($file)) { + $file = ($file === false) ? '' : $file; + if ($cache = $this->getSourceCache($file)) { return $cache->put($this->files[$file], $data); } return false; @@ -176,7 +186,11 @@ class Shared_Cache extends Cache { * @return int */ public function getId($file) { - if ($cache = $this->getSourceCache($file)) { + if ($file === false) { + return $this->storage->getSourceId(); + } + $cache = $this->getSourceCache($file); + if ($cache) { return $cache->getId($this->files[$file]); } return -1; @@ -201,6 +215,7 @@ class Shared_Cache extends Cache { * @param string $file */ public function remove($file) { + $file = ($file === false) ? '' : $file; if ($cache = $this->getSourceCache($file)) { $cache->remove($this->files[$file]); } @@ -214,7 +229,7 @@ class Shared_Cache extends Cache { */ public function move($source, $target) { if ($cache = $this->getSourceCache($source)) { - $file = \OC_Share_Backend_File::getSource($target); + $file = \OC_Share_Backend_File::getSource($target, $this->storage->getMountPoint(), $this->storage->getItemType()); if ($file && isset($file['path'])) { $cache->move($this->files[$source], $file['path']); } @@ -290,9 +305,6 @@ class Shared_Cache extends Cache { if ($file['mimetype'] === 'httpd/unix-directory') { $exploreDirs[] = ltrim($dir . '/' . $file['name'], '/'); } else if (($mimepart && $file['mimepart'] === $mimepart) || ($mimetype && $file['mimetype'] === $mimetype)) { - // usersPath not reliable - //$file['path'] = $file['usersPath']; - $file['path'] = ltrim($dir . '/' . $file['name'], '/'); $result[] = $file; } } @@ -359,6 +371,7 @@ class Shared_Cache extends Cache { * @return int */ public function calculateFolderSize($path, $entry = null) { + $path = ($path === false) ? '' : $path; if ($cache = $this->getSourceCache($path)) { return $cache->calculateFolderSize($this->files[$path]); } @@ -401,7 +414,7 @@ class Shared_Cache extends Cache { } /** - * get the path of a file on this storage by it's id + * get the path of a file on this storage relative to the mount point by it's id * * @param int $id * @param string $pathEnd (optional) used internally for recursive calls @@ -409,8 +422,9 @@ class Shared_Cache extends Cache { */ public function getPathById($id, $pathEnd = '') { // direct shares are easy - if ($path = $this->getShareById($id)) { - return $path . $pathEnd; + $path = $this->getShareById($id); + if (is_string($path)) { + return ltrim($pathEnd, '/'); } else { // if the item is a direct share we try and get the path of the parent and append the name of the item to it list($parent, $name) = $this->getParentInfo($id); diff --git a/apps/files_sharing/lib/helper.php b/apps/files_sharing/lib/helper.php index b602fe3599d..cc1f7d9ffdf 100644 --- a/apps/files_sharing/lib/helper.php +++ b/apps/files_sharing/lib/helper.php @@ -2,6 +2,9 @@ namespace OCA\Files_Sharing; +use OC_Config; +use PasswordHash; + class Helper { /** @@ -26,9 +29,6 @@ class Helper { exit; } - $type = $linkItem['item_type']; - $fileSource = $linkItem['file_source']; - $shareOwner = $linkItem['uid_owner']; $rootLinkItem = \OCP\Share::resolveReShare($linkItem); $path = null; if (isset($rootLinkItem['uid_owner'])) { @@ -61,7 +61,6 @@ class Helper { } $basePath = $path; - $rootName = basename($path); if ($relativePath !== null && \OC\Files\Filesystem::isReadable($basePath . $relativePath)) { $path .= \OC\Files\Filesystem::normalizePath($relativePath); @@ -111,4 +110,70 @@ class Helper { } return true; } + + public static function getSharesFromItem($target) { + $result = array(); + $owner = \OC\Files\Filesystem::getOwner($target); + \OC\Files\Filesystem::initMountPoints($owner); + $info = \OC\Files\Filesystem::getFileInfo($target); + $ownerView = new \OC\Files\View('/'.$owner.'/files'); + if ( $owner != \OCP\User::getUser() ) { + $path = $ownerView->getPath($info['fileid']); + } else { + $path = $target; + } + + + $ids = array(); + while ($path !== '' && $path !== '.' && $path !== '/') { + $info = $ownerView->getFileInfo($path); + $ids[] = $info['fileid']; + $path = dirname($path); + } + + if (!empty($ids)) { + + $idList = array_chunk($ids, 99, true); + + foreach ($idList as $subList) { + $statement = "SELECT `share_with`, `share_type`, `file_target` FROM `*PREFIX*share` WHERE `file_source` IN (" . implode(',', $subList) . ") AND `share_type` IN (0, 1, 2)"; + $query = \OCP\DB::prepare($statement); + $r = $query->execute(); + $result = array_merge($result, $r->fetchAll()); + } + } + + return $result; + } + + public static function getUidAndFilename($filename) { + $uid = \OC\Files\Filesystem::getOwner($filename); + \OC\Files\Filesystem::initMountPoints($uid); + if ( $uid != \OCP\User::getUser() ) { + $info = \OC\Files\Filesystem::getFileInfo($filename); + $ownerView = new \OC\Files\View('/'.$uid.'/files'); + $filename = $ownerView->getPath($info['fileid']); + } + return array($uid, $filename); + } + + /** + * @brief Format a path to be relative to the /user/files/ directory + * @param string $path the absolute path + * @return string e.g. turns '/admin/files/test.txt' into 'test.txt' + */ + public static function stripUserFilesPath($path) { + $trimmed = ltrim($path, '/'); + $split = explode('/', $trimmed); + + // it is not a file relative to data/user/files + if (count($split) < 3 || $split[1] !== 'files') { + return false; + } + + $sliced = array_slice($split, 2); + $relPath = implode('/', $sliced); + + return $relPath; + } } diff --git a/apps/files_sharing/lib/permissions.php b/apps/files_sharing/lib/permissions.php index 31b7ac361ae..c3ad63e2fd2 100644 --- a/apps/files_sharing/lib/permissions.php +++ b/apps/files_sharing/lib/permissions.php @@ -31,7 +31,9 @@ class Shared_Permissions extends Permissions { */ public function get($fileId, $user) { if ($fileId == -1) { - return \OCP\PERMISSION_READ; + // if we ask for the mount point return -1 so that we can get the correct + // permissions by the path, with the root fileId we have no idea which share is meant + return -1; } $source = \OCP\Share::getItemSharedWithBySource('file', $fileId, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE, null, true); diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php index 5e00050fe1e..c0c9e0c107e 100644 --- a/apps/files_sharing/lib/share/file.php +++ b/apps/files_sharing/lib/share/file.php @@ -2,8 +2,9 @@ /** * ownCloud * -* @author Michael Gapczynski -* @copyright 2012 Michael Gapczynski mtgap@owncloud.com +* @author Bjoern Schiessle, Michael Gapczynski +* @copyright 2012 Michael Gapczynski <mtgap@owncloud.com> + * 2014 Bjoern Schiessle <schiessle@owncloud.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -27,6 +28,7 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { const FORMAT_OPENDIR = 3; const FORMAT_GET_ALL = 4; const FORMAT_PERMISSIONS = 5; + const FORMAT_TARGET_NAMES = 6; private $path; @@ -49,24 +51,37 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { return false; } + /** + * @brief create unique target + * @param string $filePath + * @param string $shareWith + * @param string $exclude + * @return string + */ public function generateTarget($filePath, $shareWith, $exclude = null) { $target = '/'.basename($filePath); - if (isset($exclude)) { - if ($pos = strrpos($target, '.')) { - $name = substr($target, 0, $pos); - $ext = substr($target, $pos); - } else { - $name = $target; - $ext = ''; - } - $i = 2; - $append = ''; - while (in_array($name.$append.$ext, $exclude)) { - $append = ' ('.$i.')'; - $i++; - } - $target = $name.$append.$ext; + + // for group shares we return the target right away + if ($shareWith === false) { + return $target; + } + + \OC\Files\Filesystem::initMountPoints($shareWith); + $view = new \OC\Files\View('/' . $shareWith . '/files'); + $excludeList = \OCP\Share::getItemsSharedWithUser('file', $shareWith, self::FORMAT_TARGET_NAMES); + if (is_array($exclude)) { + $excludeList = array_merge($excludeList, $exclude); + } + + $pathinfo = pathinfo($target); + $ext = (isset($pathinfo['extension'])) ? '.'.$pathinfo['extension'] : ''; + $name = $pathinfo['filename']; + $i = 2; + while ($view->file_exists($target) || in_array($target, $excludeList)) { + $target = '/' . $name . ' ('.$i.')' . $ext; + $i++; } + return $target; } @@ -78,7 +93,7 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { 'path' => $items[key($items)]['path'], 'storage' => $items[key($items)]['storage'], 'permissions' => $items[key($items)]['permissions'], - 'uid_owner' => $items[key($items)]['uid_owner'] + 'uid_owner' => $items[key($items)]['uid_owner'], ); } else if ($format == self::FORMAT_GET_FOLDER_CONTENTS) { $files = array(); @@ -108,22 +123,6 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { $files[] = $file; } return $files; - } else if ($format == self::FORMAT_FILE_APP_ROOT) { - $mtime = 0; - $size = 0; - foreach ($items as $item) { - if ($item['mtime'] > $mtime) { - $mtime = $item['mtime']; - } - $size += (int)$item['size']; - } - return array( - 'fileid' => -1, - 'name' => 'Shared', - 'mtime' => $mtime, - 'mimetype' => 'httpd/unix-directory', - 'size' => $size - ); } else if ($format == self::FORMAT_OPENDIR) { $files = array(); foreach ($items as $item) { @@ -142,46 +141,60 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { $filePermissions[$item['file_source']] = $item['permissions']; } return $filePermissions; + } else if ($format === self::FORMAT_TARGET_NAMES) { + $targets = array(); + foreach ($items as $item) { + $targets[] = $item['file_target']; + } + return $targets; } return array(); } - public static function getSource($target) { - if ($target == '') { - return false; + /** + * @brief resolve reshares to return the correct source item + * @param array $source + * @return array source item + */ + protected static function resolveReshares($source) { + if (isset($source['parent'])) { + $parent = $source['parent']; + while (isset($parent)) { + $query = \OC_DB::prepare('SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?', 1); + $item = $query->execute(array($parent))->fetchRow(); + if (isset($item['parent'])) { + $parent = $item['parent']; + } else { + $fileOwner = $item['uid_owner']; + break; + } + } + } else { + $fileOwner = $source['uid_owner']; } - $target = '/'.$target; - $target = rtrim($target, '/'); - $pos = strpos($target, '/', 1); - // Get shared folder name - if ($pos !== false) { - $folder = substr($target, 0, $pos); - $source = \OCP\Share::getItemSharedWith('folder', $folder, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); - if ($source) { - $source['path'] = $source['path'].substr($target, strlen($folder)); + if (isset($fileOwner)) { + $source['fileOwner'] = $fileOwner; + } else { + \OCP\Util::writeLog('files_sharing', "No owner found for reshare", \OCP\Util::ERROR); + } + + return $source; + } + + public static function getSource($target, $mountPoint, $itemType) { + + if ($itemType === 'folder') { + $source = \OCP\Share::getItemSharedWith('folder', $mountPoint, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); + if ($source && $target !== '') { + $source['path'] = $source['path'].'/'.$target; } } else { - $source = \OCP\Share::getItemSharedWith('file', $target, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); + $source = \OCP\Share::getItemSharedWith('file', $mountPoint, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); } if ($source) { - if (isset($source['parent'])) { - $parent = $source['parent']; - while (isset($parent)) { - $query = \OC_DB::prepare('SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?', 1); - $item = $query->execute(array($parent))->fetchRow(); - if (isset($item['parent'])) { - $parent = $item['parent']; - } else { - $fileOwner = $item['uid_owner']; - break; - } - } - } else { - $fileOwner = $source['uid_owner']; - } - $source['fileOwner'] = $fileOwner; - return $source; + return self::resolveReshares($source); } + \OCP\Util::writeLog('files_sharing', 'File source not found for: '.$target, \OCP\Util::DEBUG); return false; } diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index b922654e5ec..5e478d5ead8 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -2,8 +2,9 @@ /** * ownCloud * - * @author Michael Gapczynski - * @copyright 2011 Michael Gapczynski mtgap@owncloud.com + * @author Bjoern Schiessle, Michael Gapczynski + * @copyright 2011 Michael Gapczynski <mtgap@owncloud.com> + * 2014 Bjoern Schiessle <schiessle@owncloud.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -27,15 +28,27 @@ namespace OC\Files\Storage; */ class Shared extends \OC\Files\Storage\Common { - private $sharedFolder; + private $share; // the shared resource private $files = array(); public function __construct($arguments) { - $this->sharedFolder = $arguments['sharedFolder']; + $this->share = $arguments['share']; } + /** + * @breif get id of the mount point + * @return string + */ public function getId() { - return 'shared::' . $this->sharedFolder; + return 'shared::' . $this->getMountPoint(); + } + + /** + * @breif get file cache of the shared item source + * @return string + */ + public function getSourceId() { + return $this->share['file_source']; } /** @@ -48,14 +61,14 @@ class Shared extends \OC\Files\Storage\Common { if (!isset($this->files[$target])) { // Check for partial files if (pathinfo($target, PATHINFO_EXTENSION) === 'part') { - $source = \OC_Share_Backend_File::getSource(substr($target, 0, -5)); + $source = \OC_Share_Backend_File::getSource(substr($target, 0, -5), $this->getMountPoint(), $this->getItemType()); if ($source) { $source['path'] .= '.part'; // All partial files have delete permission $source['permissions'] |= \OCP\PERMISSION_DELETE; } } else { - $source = \OC_Share_Backend_File::getSource($target); + $source = \OC_Share_Backend_File::getSource($target, $this->getMountPoint(), $this->getItemType()); } $this->files[$target] = $source; } @@ -117,25 +130,15 @@ class Shared extends \OC\Files\Storage\Common { } public function opendir($path) { - if ($path == '' || $path == '/') { - $files = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_Folder::FORMAT_OPENDIR); - \OC\Files\Stream\Dir::register('shared', $files); - return opendir('fakedir://shared'); - } else if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - return $storage->opendir($internalPath); - } - return false; + $source = $this->getSourcePath($path); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->opendir($internalPath); } public function is_dir($path) { - if ($path == '' || $path == '/') { - return true; - } else if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - return $storage->is_dir($internalPath); - } - return false; + $source = $this->getSourcePath($path); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->is_dir($internalPath); } public function is_file($path) { @@ -180,7 +183,7 @@ class Shared extends \OC\Files\Storage\Common { public function isCreatable($path) { if ($path == '') { - return false; + $path = $this->getMountPoint(); } return ($this->getPermissions($path) & \OCP\PERMISSION_CREATE); } @@ -191,21 +194,21 @@ class Shared extends \OC\Files\Storage\Common { public function isUpdatable($path) { if ($path == '') { - return false; + $path = $this->getMountPoint(); } return ($this->getPermissions($path) & \OCP\PERMISSION_UPDATE); } public function isDeletable($path) { if ($path == '') { - return true; + $path = $this->getMountPoint(); } return ($this->getPermissions($path) & \OCP\PERMISSION_DELETE); } public function isSharable($path) { if ($path == '') { - return false; + $path = $this->getMountPoint(); } return ($this->getPermissions($path) & \OCP\PERMISSION_SHARE); } @@ -221,32 +224,16 @@ class Shared extends \OC\Files\Storage\Common { } public function filemtime($path) { - if ($path == '' || $path == '/') { - $mtime = 0; - $dh = $this->opendir($path); - if (is_resource($dh)) { - while (($filename = readdir($dh)) !== false) { - $tempmtime = $this->filemtime($filename); - if ($tempmtime > $mtime) { - $mtime = $tempmtime; - } - } - } - return $mtime; - } else { - $source = $this->getSourcePath($path); - if ($source) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - return $storage->filemtime($internalPath); - } - } + $source = $this->getSourcePath($path); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->filemtime($internalPath); } public function file_get_contents($path) { $source = $this->getSourcePath($path); if ($source) { $info = array( - 'target' => $this->sharedFolder . $path, + 'target' => $this->getMountPoint() . $path, 'source' => $source, ); \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_get_contents', $info); @@ -264,7 +251,7 @@ class Shared extends \OC\Files\Storage\Common { return false; } $info = array( - 'target' => $this->sharedFolder . $path, + 'target' => $this->getMountPoint() . '/' . $path, 'source' => $source, ); \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_put_contents', $info); @@ -277,6 +264,7 @@ class Shared extends \OC\Files\Storage\Common { public function unlink($path) { // Delete the file if DELETE permission is granted + $path = ($path === false) ? '' : $path; if ($source = $this->getSourcePath($path)) { if ($this->isDeletable($path)) { list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); @@ -286,23 +274,117 @@ class Shared extends \OC\Files\Storage\Common { return false; } + /** + * @brief Format a path to be relative to the /user/files/ directory + * @param string $path the absolute path + * @return string e.g. turns '/admin/files/test.txt' into '/test.txt' + */ + private static function stripUserFilesPath($path) { + $trimmed = ltrim($path, '/'); + $split = explode('/', $trimmed); + + // it is not a file relative to data/user/files + if (count($split) < 3 || $split[1] !== 'files') { + \OCP\Util::writeLog('file sharing', + 'Can not strip userid and "files/" from path: ' . $path, + \OCP\Util::DEBUG); + return false; + } + + // skip 'user' and 'files' + $sliced = array_slice($split, 2); + $relPath = implode('/', $sliced); + + return '/' . $relPath; + } + + /** + * @brief rename a shared folder/file + * @param string $sourcePath + * @param string $targetPath + * @return bool + */ + private function renameMountPoint($sourcePath, $targetPath) { + + // it shouldn't be possible to move a Shared storage into another one + list($targetStorage, ) = \OC\Files\Filesystem::resolvePath($targetPath); + if ($targetStorage instanceof \OC\Files\Storage\Shared) { + \OCP\Util::writeLog('file sharing', + 'It is not allowed to move one mount point into another one', + \OCP\Util::DEBUG); + return false; + } + + $relTargetPath = $this->stripUserFilesPath($targetPath); + + // if the user renames a mount point from a group share we need to create a new db entry + // for the unique name + if ($this->getShareType() === \OCP\Share::SHARE_TYPE_GROUP && $this->uniqueNameSet() === false) { + $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` (`item_type`, `item_source`, `item_target`,' + .' `share_type`, `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`,' + .' `file_target`, `token`, `parent`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)'); + $arguments = array($this->share['item_type'], $this->share['item_source'], $this->share['item_target'], + 2, \OCP\User::getUser(), $this->share['uid_owner'], $this->share['permissions'], $this->share['stime'], $this->share['file_source'], + $relTargetPath, $this->share['token'], $this->share['id']); + + } else { + // rename mount point + $query = \OC_DB::prepare( + 'Update `*PREFIX*share` + SET `file_target` = ? + WHERE `id` = ?' + ); + $arguments = array($relTargetPath, $this->getShareId()); + } + + $result = $query->execute($arguments); + + if ($result) { + // update the mount manager with the new paths + $mountManager = \OC\Files\Filesystem::getMountManager(); + $mount = $mountManager->find($sourcePath); + $mount->setMountPoint($targetPath . '/'); + $mountManager->addMount($mount); + $mountManager->removeMount($sourcePath . '/'); + $this->setUniqueName(); + $this->setMountPoint($relTargetPath); + + } else { + \OCP\Util::writeLog('file sharing', + 'Could not rename mount point for shared folder "' . $sourcePath . '" to "' . $targetPath . '"', + \OCP\Util::ERROR); + } + + return $result; + } + + public function rename($path1, $path2) { - // Renaming/moving is only allowed within shared folders - $pos1 = strpos($path1, '/', 1); - $pos2 = strpos($path2, '/', 1); - if ($pos1 !== false && $pos2 !== false && ($oldSource = $this->getSourcePath($path1))) { - $newSource = $this->getSourcePath(dirname($path2)) . '/' . basename($path2); - // Within the same folder, we only need UPDATE permissions - if (dirname($path1) == dirname($path2) and $this->isUpdatable($path1)) { - list($storage, $oldInternalPath) = \OC\Files\Filesystem::resolvePath($oldSource); - list(, $newInternalPath) = \OC\Files\Filesystem::resolvePath($newSource); - return $storage->rename($oldInternalPath, $newInternalPath); + + $sourceMountPoint = \OC\Files\Filesystem::getMountPoint($path1); + $targetMountPoint = \OC\Files\Filesystem::getMountPoint($path2); + $relPath1 = \OCA\Files_Sharing\Helper::stripUserFilesPath($path1); + $relPath2 = \OCA\Files_Sharing\Helper::stripUserFilesPath($path2); + + // if we renamed the mount point we need to adjust the file_target in the + // database + if (\OC\Files\Filesystem::normalizePath($sourceMountPoint) === \OC\Files\Filesystem::normalizePath($path1)) { + return $this->renameMountPoint($path1, $path2); + } + + + if ( // Within the same mount point, we only need UPDATE permissions + ($sourceMountPoint === $targetMountPoint && $this->isUpdatable($sourceMountPoint)) || // otherwise DELETE and CREATE permissions required - } elseif ($this->isDeletable($path1) && $this->isCreatable(dirname($path2))) { - $rootView = new \OC\Files\View(''); - return $rootView->rename($oldSource, $newSource); - } + ($this->isDeletable($path1) && $this->isCreatable(dirname($path2)))) { + + list($user1, $path1) = \OCA\Files_Sharing\Helper::getUidAndFilename($relPath1); + $targetFilename = basename($relPath2); + list($user2, $path2) = \OCA\Files_Sharing\Helper::getUidAndFilename(dirname($relPath2)); + $rootView = new \OC\Files\View(''); + return $rootView->rename('/' . $user1 . '/files/' . $path1, '/' . $user2 . '/files/' . $path2 . '/' . $targetFilename); } + return false; } @@ -343,7 +425,7 @@ class Shared extends \OC\Files\Storage\Common { } } $info = array( - 'target' => $this->sharedFolder . $path, + 'target' => $this->getMountPoint() . $path, 'source' => $source, 'mode' => $mode, ); @@ -355,9 +437,6 @@ class Shared extends \OC\Files\Storage\Common { } public function getMimeType($path) { - if ($path == '' || $path == '/') { - return 'httpd/unix-directory'; - } if ($source = $this->getSourcePath($path)) { list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->getMimeType($internalPath); @@ -367,13 +446,14 @@ class Shared extends \OC\Files\Storage\Common { public function free_space($path) { if ($path == '') { - return \OC\Files\SPACE_UNKNOWN; + $path = $this->getMountPoint(); } $source = $this->getSourcePath($path); if ($source) { list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->free_space($internalPath); } + return \OC\Files\SPACE_UNKNOWN; } public function getLocalFile($path) { @@ -393,20 +473,80 @@ class Shared extends \OC\Files\Storage\Common { } public static function setup($options) { + $shares = \OCP\Share::getItemsSharedWith('file'); if (!\OCP\User::isLoggedIn() || \OCP\User::getUser() != $options['user'] - || \OCP\Share::getItemsSharedWith('file') + || $shares ) { - $user_dir = $options['user_dir']; - \OC\Files\Filesystem::mount('\OC\Files\Storage\Shared', - array('sharedFolder' => '/Shared'), - $user_dir . '/Shared/'); + foreach ($shares as $share) { + \OC\Files\Filesystem::mount('\OC\Files\Storage\Shared', + array( + 'share' => $share, + ), + $options['user_dir'] . '/' . $share['file_target']); + } } } + /** + * @brief return mount point of share, relative to data/user/files + * @return string + */ + public function getMountPoint() { + return $this->share['file_target']; + } + + /** + * @brief get share type + * @return integer can be single user share (0) group share (1), unique group share name (2) + */ + private function getShareType() { + return $this->share['share_type']; + } + + private function setMountPoint($path) { + $this->share['file_target'] = $path; + } + + /** + * @brief does the group share already has a user specific unique name + * @return bool + */ + private function uniqueNameSet() { + return (isset($this->share['unique_name']) && $this->share['unique_name']); + } + + /** + * @brief the share now uses a unique name of this user + */ + private function setUniqueName() { + $this->share['unique_name'] = true; + } + + /** + * @brief get share ID + * @return integer unique share ID + */ + private function getShareId() { + return $this->share['id']; + } + + /** + * @brief get the user who shared the file + * @return string + */ + public function getSharedFrom() { + return $this->share['uid_owner']; + } + + /** + * @brief return share type, can be "file" or "folder" + * @return string + */ + public function getItemType() { + return $this->share['item_type']; + } + public function hasUpdated($path, $time) { - if ($path == '') { - return false; - } return $this->filemtime($path) > $time; } @@ -428,7 +568,7 @@ class Shared extends \OC\Files\Storage\Common { public function getOwner($path) { if ($path == '') { - return false; + $path = $this->getMountPoint(); } $source = $this->getFile($path); if ($source) { @@ -439,7 +579,7 @@ class Shared extends \OC\Files\Storage\Common { public function getETag($path) { if ($path == '') { - return parent::getETag($path); + $path = $this->getMountPoint(); } if ($source = $this->getSourcePath($path)) { list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); diff --git a/apps/files_sharing/lib/updater.php b/apps/files_sharing/lib/updater.php index e3a7679292d..f7c0a75aeeb 100644 --- a/apps/files_sharing/lib/updater.php +++ b/apps/files_sharing/lib/updater.php @@ -27,30 +27,47 @@ class Shared_Updater { static private $toRemove = array(); /** + * @brief walk up the users file tree and update the etags + * @param string $user + * @param string $path + */ + static private function correctUsersFolder($user, $path) { + // $path points to the mount point which is a virtual folder, so we start with + // the parent + $path = '/files' . dirname($path); + \OC\Files\Filesystem::initMountPoints($user); + $view = new \OC\Files\View('/' . $user); + if ($view->file_exists($path)) { + while ($path !== '/') { + $etag = $view->getETag($path); + $view->putFileInfo($path, array('etag' => $etag)); + $path = dirname($path); + } + } else { + error_log("error!" . 'can not update etags on ' . $path . ' for user ' . $user); + \OCP\Util::writeLog('files_sharing', 'can not update etags on ' . $path . ' for user ' . $user, \OCP\Util::ERROR); + } + } + + /** * Correct the parent folders' ETags for all users shared the file at $target * * @param string $target */ static public function correctFolders($target) { - $uid = \OCP\User::getUser(); - $uidOwner = \OC\Files\Filesystem::getOwner($target); - $info = \OC\Files\Filesystem::getFileInfo($target); - $checkedUser = array($uidOwner); // Correct Shared folders of other users shared with - $users = \OCP\Share::getUsersItemShared('file', $info['fileid'], $uidOwner, true); - if (!empty($users)) { - while (!empty($users)) { - $reshareUsers = array(); + $shares = \OCA\Files_Sharing\Helper::getSharesFromItem($target); + + foreach ($shares as $share) { + if ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_USER) { + self::correctUsersFolder($share['share_with'], $share['file_target']); + } elseif ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) { + $users = \OC_Group::usersInGroup($share['share_with']); foreach ($users as $user) { - if ( !in_array($user, $checkedUser) ) { - $etag = \OC\Files\Filesystem::getETag(''); - \OCP\Config::setUserValue($user, 'files_sharing', 'etag', $etag); - // Look for reshares - $reshareUsers = array_merge($reshareUsers, \OCP\Share::getUsersItemShared('file', $info['fileid'], $user, true)); - $checkedUser[] = $user; - } + self::correctUsersFolder($user, $share['file_target']); } - $users = $reshareUsers; + } else { //unique name for group share + self::correctUsersFolder($share['share_with'], $share['file_target']); } } } @@ -108,34 +125,6 @@ class Shared_Updater { } /** - * @param array $params - */ - static public function shareHook($params) { - if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') { - if (isset($params['uidOwner'])) { - $uidOwner = $params['uidOwner']; - } else { - $uidOwner = \OCP\User::getUser(); - } - $users = \OCP\Share::getUsersItemShared($params['itemType'], $params['fileSource'], $uidOwner, true, false); - if (!empty($users)) { - while (!empty($users)) { - $reshareUsers = array(); - foreach ($users as $user) { - if ($user !== $uidOwner) { - $etag = \OC\Files\Filesystem::getETag(''); - \OCP\Config::setUserValue($user, 'files_sharing', 'etag', $etag); - // Look for reshares - $reshareUsers = array_merge($reshareUsers, \OCP\Share::getUsersItemShared('file', $params['fileSource'], $user, true)); - } - } - $users = $reshareUsers; - } - } - } - } - - /** * clean up oc_share table from files which are no longer exists * * This fixes issues from updates from files_sharing < 0.3.5.6 (ownCloud 4.5) diff --git a/apps/files_sharing/lib/watcher.php b/apps/files_sharing/lib/watcher.php index 285b1a58c6e..11d3ce1cabd 100644 --- a/apps/files_sharing/lib/watcher.php +++ b/apps/files_sharing/lib/watcher.php @@ -32,7 +32,7 @@ class Shared_Watcher extends Watcher { * @param string $path */ public function checkUpdate($path) { - if ($path != '' && parent::checkUpdate($path) === true) { + if (parent::checkUpdate($path) === true) { // since checkUpdate() has already updated the size of the subdirs, // only apply the update to the owner's parent dirs |