Skip to content
90 changes: 87 additions & 3 deletions apps/files_sharing/lib/MountProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,20 @@ public function getMountsForUser(IUser $user, IStorageFactory $storageFactory) {
return $share->getPermissions() > 0 && $share->getShareOwner() !== $user->getUID();
});

$mounts = [];
foreach ($shares as $share) {
$superShares = $this->buildSuperShares($shares, $user);

$mounts = [];
foreach ($superShares as $share) {
try {
$mounts[] = new SharedMount(
'\OC\Files\Storage\Shared',
$mounts,
[
'user' => $user->getUID(),
'newShare' => $share,
// parent share
'superShare' => $share[0],
// children/component of the superShare
'groupedShares' => $share[1],
],
$storageFactory
);
Expand All @@ -97,4 +101,84 @@ public function getMountsForUser(IUser $user, IStorageFactory $storageFactory) {
// array_filter removes the null values from the array
return array_filter($mounts);
}

/**
* Groups shares by path (nodeId) and target path
*
* @param \OCP\Share\IShare[] $shares
* @return \OCP\Share\IShare[][] array of grouped shares, each element in the
* array is a group which itself is an array of shares
*/
private function groupShares(array $shares) {
$tmp = [];

foreach ($shares as $share) {
if (!isset($tmp[$share->getNodeId()])) {
$tmp[$share->getNodeId()] = [];
}
$tmp[$share->getNodeId()][] = $share;
}

$result = [];
// sort by stime, the super share will be based on the least recent share
foreach ($tmp as &$tmp2) {
@usort($tmp2, function($a, $b) {
if ($a->getShareTime() < $b->getShareTime()) {
return -1;
}
return 1;
});
$result[] = $tmp2;
}

return array_values($result);
}

/**
* Build super shares (virtual share) by grouping them by node id and target,
* then for each group compute the super share and return it along with the matching
* grouped shares. The most permissive permissions are used based on the permissions
* of all shares within the group.
*
* @param \OCP\Share\IShare[] $allShares
* @param \OCP\IUser $user user
* @return array Tuple of [superShare, groupedShares]
*/
private function buildSuperShares(array $allShares, \OCP\IUser $user) {
$result = [];

$groupedShares = $this->groupShares($allShares);

/** @var \OCP\Share\IShare[] $shares */
foreach ($groupedShares as $shares) {
if (count($shares) === 0) {
continue;
}

$superShare = $this->shareManager->newShare();

// compute super share based on first entry of the group
$superShare->setId($shares[0]->getId())
->setShareOwner($shares[0]->getShareOwner())
->setNodeId($shares[0]->getNodeId())
->setTarget($shares[0]->getTarget());

// use most permissive permissions
$permissions = 0;
foreach ($shares as $share) {
$permissions |= $share->getPermissions();
if ($share->getTarget() !== $superShare->getTarget()) {
// adjust target, for database consistency
$share->setTarget($superShare->getTarget());
$this->shareManager->moveShare($share, $user->getUID());
}
}

$superShare->setPermissions($permissions);

$result[] = [$superShare, $shares];
}

return $result;
}
}
24 changes: 17 additions & 7 deletions apps/files_sharing/lib/SharedMount.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ class SharedMount extends MountPoint implements MoveableMount {
private $user;

/** @var \OCP\Share\IShare */
private $share;
private $superShare;

/** @var \OCP\Share\IShare[] */
private $groupedShares;

/**
* @param string $storage
Expand All @@ -63,10 +66,13 @@ class SharedMount extends MountPoint implements MoveableMount {
public function __construct($storage, array $mountpoints, $arguments = null, $loader = null) {
$this->user = $arguments['user'];
$this->recipientView = new View('/' . $this->user . '/files');
$this->share = $arguments['newShare'];
$newMountPoint = $this->verifyMountPoint($this->share, $mountpoints);

$this->superShare = $arguments['superShare'];
$this->groupedShares = $arguments['groupedShares'];

$newMountPoint = $this->verifyMountPoint($this->superShare, $mountpoints);
$absMountPoint = '/' . $this->user . '/files' . $newMountPoint;
$arguments['ownerView'] = new View('/' . $this->share->getShareOwner() . '/files');
$arguments['ownerView'] = new View('/' . $this->superShare->getShareOwner() . '/files');
parent::__construct($storage, $absMountPoint, $arguments, $loader);
}

Expand Down Expand Up @@ -108,7 +114,11 @@ private function verifyMountPoint(\OCP\Share\IShare $share, array $mountpoints)
*/
private function updateFileTarget($newPath, &$share) {
$share->setTarget($newPath);
\OC::$server->getShareManager()->moveShare($share, $this->user);

foreach ($this->groupedShares as $share) {
$share->setTarget($newPath);
\OC::$server->getShareManager()->moveShare($share, $this->user);
}
}


Expand Down Expand Up @@ -214,7 +224,7 @@ public function removeMount() {
* @return \OCP\Share\IShare
*/
public function getShare() {
return $this->share;
return $this->superShare;
}

/**
Expand All @@ -223,6 +233,6 @@ public function getShare() {
* @return int
*/
public function getStorageRootId() {
return $this->share->getNodeId();
return $this->getShare()->getNodeId();
}
}
77 changes: 34 additions & 43 deletions apps/files_sharing/lib/sharedstorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@
* Convert target path to source path and pass the function call to the correct storage provider
*/
class Shared extends \OC\Files\Storage\Wrapper\Jail implements ISharedStorage {

private $share; // the shared resource

/** @var \OCP\Share\IShare */
private $newShare;
private $superShare;

/** @var \OCP\Share\IShare[] */
private $groupedShares;

/**
* @var \OC\Files\View
Expand Down Expand Up @@ -77,11 +77,14 @@ class Shared extends \OC\Files\Storage\Wrapper\Jail implements ISharedStorage {
public function __construct($arguments) {
$this->ownerView = $arguments['ownerView'];
$this->logger = \OC::$server->getLogger();
$this->newShare = $arguments['newShare'];

$this->superShare = $arguments['superShare'];
$this->groupedShares = $arguments['groupedShares'];

$this->user = $arguments['user'];

Filesystem::initMountPoints($this->newShare->getShareOwner());
$sourcePath = $this->ownerView->getPath($this->newShare->getNodeId());
Filesystem::initMountPoints($this->superShare->getShareOwner());
$sourcePath = $this->ownerView->getPath($this->superShare->getNodeId());
list($storage, $internalPath) = $this->ownerView->resolvePath($sourcePath);

parent::__construct([
Expand All @@ -96,15 +99,22 @@ private function init() {
}
$this->initialized = true;
try {
Filesystem::initMountPoints($this->newShare->getShareOwner());
$sourcePath = $this->ownerView->getPath($this->newShare->getNodeId());
Filesystem::initMountPoints($this->superShare->getShareOwner());
$sourcePath = $this->ownerView->getPath($this->superShare->getNodeId());
list($this->sourceStorage, $sourceInternalPath) = $this->ownerView->resolvePath($sourcePath);
$this->sourceRootInfo = $this->sourceStorage->getCache()->get($sourceInternalPath);
} catch (\Exception $e) {
$this->logger->logException($e);
}
}

/**
* @return string
*/
public function getShareId() {
return $this->superShare->getId();
}

private function isValid() {
$this->init();
return $this->sourceRootInfo && ($this->sourceRootInfo->getPermissions() & Constants::PERMISSION_SHARE) === Constants::PERMISSION_SHARE;
Expand All @@ -119,15 +129,6 @@ public function getId() {
return 'shared::' . $this->getMountPoint();
}

/**
* get file cache of the shared item source
*
* @return int
*/
public function getSourceId() {
return $this->newShare->getNodeId();
}

/**
* Get the permissions granted for a shared file
*
Expand All @@ -138,7 +139,7 @@ public function getPermissions($target = '') {
if (!$this->isValid()) {
return 0;
}
$permissions = $this->newShare->getPermissions();
$permissions = $this->superShare->getPermissions();
// part files and the mount point always have delete permissions
if ($target === '' || pathinfo($target, PATHINFO_EXTENSION) === 'part') {
$permissions |= \OCP\Constants::PERMISSION_DELETE;
Expand Down Expand Up @@ -260,30 +261,18 @@ public function rename($path1, $path2) {
* @return string
*/
public function getMountPoint() {
return $this->newShare->getTarget();
return $this->superShare->getTarget();
}

/**
* @param string $path
*/
public function setMountPoint($path) {
$this->newShare->setTarget($path);
}

/**
* @return int
*/
public function getShareType() {
return $this->newShare->getShareType();
}
$this->superShare->setTarget($path);

/**
* get share ID
*
* @return integer unique share ID
*/
public function getShareId() {
return $this->newShare->getId();
foreach ($this->groupedShares as $share) {
$share->setTarget($path);
}
}

/**
Expand All @@ -292,14 +281,14 @@ public function getShareId() {
* @return string
*/
public function getSharedFrom() {
return $this->newShare->getShareOwner();
return $this->superShare->getShareOwner();
}

/**
* @return \OCP\Share\IShare
*/
public function getShare() {
return $this->newShare;
return $this->superShare;
}

/**
Expand All @@ -308,7 +297,7 @@ public function getShare() {
* @return string
*/
public function getItemType() {
return $this->newShare->getNodeType();
return $this->superShare->getNodeType();
}

public function getCache($path = '', $storage = null) {
Expand Down Expand Up @@ -337,7 +326,7 @@ public function getPropagator($storage = null) {
}

public function getOwner($path) {
return $this->newShare->getShareOwner();
return $this->superShare->getShareOwner();
}

/**
Expand All @@ -346,7 +335,9 @@ public function getOwner($path) {
* @return bool
*/
public function unshareStorage() {
\OC::$server->getShareManager()->deleteFromSelf($this->newShare, $this->user);
foreach ($this->groupedShares as $share) {
\OC::$server->getShareManager()->deleteFromSelf($share, $this->user);
}
return true;
}

Expand All @@ -362,7 +353,7 @@ public function acquireLock($path, $type, ILockingProvider $provider) {
$targetStorage->acquireLock($targetInternalPath, $type, $provider);
// lock the parent folders of the owner when locking the share as recipient
if ($path === '') {
$sourcePath = $this->ownerView->getPath($this->newShare->getNodeId());
$sourcePath = $this->ownerView->getPath($this->superShare->getNodeId());
$this->ownerView->lockFile(dirname($sourcePath), ILockingProvider::LOCK_SHARED, true);
}
}
Expand All @@ -378,7 +369,7 @@ public function releaseLock($path, $type, ILockingProvider $provider) {
$targetStorage->releaseLock($targetInternalPath, $type, $provider);
// unlock the parent folders of the owner when unlocking the share as recipient
if ($path === '') {
$sourcePath = $this->ownerView->getPath($this->newShare->getNodeId());
$sourcePath = $this->ownerView->getPath($this->superShare->getNodeId());
$this->ownerView->unlockFile(dirname($sourcePath), ILockingProvider::LOCK_SHARED, true);
}
}
Expand Down
Loading