diff options
author | Roeland Jago Douma <rullzer@owncloud.com> | 2016-03-20 14:12:36 +0100 |
---|---|---|
committer | Roeland Jago Douma <rullzer@owncloud.com> | 2016-03-20 14:12:36 +0100 |
commit | c6edbfecbee709e752400e0b085e930c51223dcc (patch) | |
tree | 7c9019bf0f80ef0ac191a308301cd8e068f95c9d /lib/private/Share20 | |
parent | a6c921267e00d0fb5021e8bdbd4d202931d7a58a (diff) | |
download | nextcloud-server-c6edbfecbee709e752400e0b085e930c51223dcc.tar.gz nextcloud-server-c6edbfecbee709e752400e0b085e930c51223dcc.zip |
Move (new) sharing over to composer autoloader
Diffstat (limited to 'lib/private/Share20')
-rw-r--r-- | lib/private/Share20/DefaultShareProvider.php | 852 | ||||
-rw-r--r-- | lib/private/Share20/Exception/BackendError.php | 25 | ||||
-rw-r--r-- | lib/private/Share20/Exception/InvalidShare.php | 25 | ||||
-rw-r--r-- | lib/private/Share20/Exception/ProviderException.php | 27 | ||||
-rw-r--r-- | lib/private/Share20/Manager.php | 1150 | ||||
-rw-r--r-- | lib/private/Share20/ProviderFactory.php | 163 | ||||
-rw-r--r-- | lib/private/Share20/Share.php | 389 |
7 files changed, 2631 insertions, 0 deletions
diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php new file mode 100644 index 00000000000..b91ac24d44f --- /dev/null +++ b/lib/private/Share20/DefaultShareProvider.php @@ -0,0 +1,852 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * @author Roeland Jago Douma <rullzer@owncloud.com> + * + * @copyright Copyright (c) 2016, 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\Share20; + +use OCP\Files\File; +use OCP\Share\IShareProvider; +use OC\Share20\Exception\InvalidShare; +use OC\Share20\Exception\ProviderException; +use OCP\Share\Exceptions\ShareNotFound; +use OC\Share20\Exception\BackendError; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\Files\NotFoundException; +use OCP\IGroup; +use OCP\IGroupManager; +use OCP\IUserManager; +use OCP\Files\IRootFolder; +use OCP\IDBConnection; +use OCP\Files\Node; + +/** + * Class DefaultShareProvider + * + * @package OC\Share20 + */ +class DefaultShareProvider implements IShareProvider { + + // Special share type for user modified group shares + const SHARE_TYPE_USERGROUP = 2; + + /** @var IDBConnection */ + private $dbConn; + + /** @var IUserManager */ + private $userManager; + + /** @var IGroupManager */ + private $groupManager; + + /** @var IRootFolder */ + private $rootFolder; + + /** + * DefaultShareProvider constructor. + * + * @param IDBConnection $connection + * @param IUserManager $userManager + * @param IGroupManager $groupManager + * @param IRootFolder $rootFolder + */ + public function __construct( + IDBConnection $connection, + IUserManager $userManager, + IGroupManager $groupManager, + IRootFolder $rootFolder) { + $this->dbConn = $connection; + $this->userManager = $userManager; + $this->groupManager = $groupManager; + $this->rootFolder = $rootFolder; + } + + /** + * Return the identifier of this provider. + * + * @return string Containing only [a-zA-Z0-9] + */ + public function identifier() { + return 'ocinternal'; + } + + /** + * Share a path + * + * @param \OCP\Share\IShare $share + * @return \OCP\Share\IShare The share object + * @throws ShareNotFound + * @throws \Exception + */ + public function create(\OCP\Share\IShare $share) { + $qb = $this->dbConn->getQueryBuilder(); + + $qb->insert('share'); + $qb->setValue('share_type', $qb->createNamedParameter($share->getShareType())); + + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { + //Set the UID of the user we share with + $qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith())); + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { + //Set the GID of the group we share with + $qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith())); + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { + //Set the token of the share + $qb->setValue('token', $qb->createNamedParameter($share->getToken())); + + //If a password is set store it + if ($share->getPassword() !== null) { + $qb->setValue('share_with', $qb->createNamedParameter($share->getPassword())); + } + + //If an expiration date is set store it + if ($share->getExpirationDate() !== null) { + $qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime')); + } + + if (method_exists($share, 'getParent')) { + $qb->setValue('parent', $qb->createNamedParameter($share->getParent())); + } + } else { + throw new \Exception('invalid share type!'); + } + + // Set what is shares + $qb->setValue('item_type', $qb->createParameter('itemType')); + if ($share->getNode() instanceof \OCP\Files\File) { + $qb->setParameter('itemType', 'file'); + } else { + $qb->setParameter('itemType', 'folder'); + } + + // Set the file id + $qb->setValue('item_source', $qb->createNamedParameter($share->getNode()->getId())); + $qb->setValue('file_source', $qb->createNamedParameter($share->getNode()->getId())); + + // set the permissions + $qb->setValue('permissions', $qb->createNamedParameter($share->getPermissions())); + + // Set who created this share + $qb->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy())); + + // Set who is the owner of this file/folder (and this the owner of the share) + $qb->setValue('uid_owner', $qb->createNamedParameter($share->getShareOwner())); + + // Set the file target + $qb->setValue('file_target', $qb->createNamedParameter($share->getTarget())); + + // Set the time this share was created + $qb->setValue('stime', $qb->createNamedParameter(time())); + + // insert the data and fetch the id of the share + $this->dbConn->beginTransaction(); + $qb->execute(); + $id = $this->dbConn->lastInsertId('*PREFIX*share'); + $this->dbConn->commit(); + + // Now fetch the inserted share and create a complete share object + $qb = $this->dbConn->getQueryBuilder(); + $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); + + $cursor = $qb->execute(); + $data = $cursor->fetch(); + $cursor->closeCursor(); + + if ($data === false) { + throw new ShareNotFound(); + } + + $share = $this->createShare($data); + return $share; + } + + /** + * Update a share + * + * @param \OCP\Share\IShare $share + * @return \OCP\Share\IShare The share object + */ + public function update(\OCP\Share\IShare $share) { + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { + /* + * We allow updating the recipient on user shares. + */ + $qb = $this->dbConn->getQueryBuilder(); + $qb->update('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) + ->set('share_with', $qb->createNamedParameter($share->getSharedWith())) + ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) + ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) + ->set('permissions', $qb->createNamedParameter($share->getPermissions())) + ->set('item_source', $qb->createNamedParameter($share->getNode()->getId())) + ->set('file_source', $qb->createNamedParameter($share->getNode()->getId())) + ->execute(); + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { + $qb = $this->dbConn->getQueryBuilder(); + $qb->update('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) + ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) + ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) + ->set('permissions', $qb->createNamedParameter($share->getPermissions())) + ->set('item_source', $qb->createNamedParameter($share->getNode()->getId())) + ->set('file_source', $qb->createNamedParameter($share->getNode()->getId())) + ->execute(); + + /* + * Update all user defined group shares + */ + $qb = $this->dbConn->getQueryBuilder(); + $qb->update('share') + ->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) + ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) + ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) + ->set('item_source', $qb->createNamedParameter($share->getNode()->getId())) + ->set('file_source', $qb->createNamedParameter($share->getNode()->getId())) + ->execute(); + + /* + * Now update the permissions for all children that have not set it to 0 + */ + $qb = $this->dbConn->getQueryBuilder(); + $qb->update('share') + ->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) + ->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0))) + ->set('permissions', $qb->createNamedParameter($share->getPermissions())) + ->execute(); + + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { + $qb = $this->dbConn->getQueryBuilder(); + $qb->update('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) + ->set('share_with', $qb->createNamedParameter($share->getPassword())) + ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) + ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) + ->set('permissions', $qb->createNamedParameter($share->getPermissions())) + ->set('item_source', $qb->createNamedParameter($share->getNode()->getId())) + ->set('file_source', $qb->createNamedParameter($share->getNode()->getId())) + ->set('token', $qb->createNamedParameter($share->getToken())) + ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) + ->execute(); + } + + return $share; + } + + /** + * Get all children of this share + * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in + * + * @param \OCP\Share\IShare $parent + * @return \OCP\Share\IShare[] + */ + public function getChildren(\OCP\Share\IShare $parent) { + $children = []; + + $qb = $this->dbConn->getQueryBuilder(); + $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId()))) + ->andWhere( + $qb->expr()->in( + 'share_type', + $qb->createNamedParameter([ + \OCP\Share::SHARE_TYPE_USER, + \OCP\Share::SHARE_TYPE_GROUP, + \OCP\Share::SHARE_TYPE_LINK, + ], IQueryBuilder::PARAM_INT_ARRAY) + ) + ) + ->andWhere($qb->expr()->orX( + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) + )) + ->orderBy('id'); + + $cursor = $qb->execute(); + while($data = $cursor->fetch()) { + $children[] = $this->createShare($data); + } + $cursor->closeCursor(); + + return $children; + } + + /** + * Delete a share + * + * @param \OCP\Share\IShare $share + */ + public function delete(\OCP\Share\IShare $share) { + $qb = $this->dbConn->getQueryBuilder(); + $qb->delete('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))); + + /* + * If the share is a group share delete all possible + * user defined groups shares. + */ + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { + $qb->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))); + } + + $qb->execute(); + } + + /** + * Unshare a share from the recipient. If this is a group share + * this means we need a special entry in the share db. + * + * @param \OCP\Share\IShare $share + * @param string $recipient UserId of recipient + * @throws BackendError + * @throws ProviderException + */ + public function deleteFromSelf(\OCP\Share\IShare $share, $recipient) { + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { + + $group = $this->groupManager->get($share->getSharedWith()); + $user = $this->userManager->get($recipient); + + if (!$group->inGroup($user)) { + throw new ProviderException('Recipient not in receiving group'); + } + + // Try to fetch user specific share + $qb = $this->dbConn->getQueryBuilder(); + $stmt = $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))) + ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient))) + ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) + ->andWhere($qb->expr()->orX( + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) + )) + ->execute(); + + $data = $stmt->fetch(); + + /* + * Check if there already is a user specific group share. + * If there is update it (if required). + */ + if ($data === false) { + $qb = $this->dbConn->getQueryBuilder(); + + $type = $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder'; + + //Insert new share + $qb->insert('share') + ->values([ + 'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP), + 'share_with' => $qb->createNamedParameter($recipient), + 'uid_owner' => $qb->createNamedParameter($share->getShareOwner()), + 'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()), + 'parent' => $qb->createNamedParameter($share->getId()), + 'item_type' => $qb->createNamedParameter($type), + 'item_source' => $qb->createNamedParameter($share->getNode()->getId()), + 'file_source' => $qb->createNamedParameter($share->getNode()->getId()), + 'file_target' => $qb->createNamedParameter($share->getTarget()), + 'permissions' => $qb->createNamedParameter(0), + 'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()), + ])->execute(); + + } else if ($data['permissions'] !== 0) { + + // Update existing usergroup share + $qb = $this->dbConn->getQueryBuilder(); + $qb->update('share') + ->set('permissions', $qb->createNamedParameter(0)) + ->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id']))) + ->execute(); + } + + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { + + if ($share->getSharedWith() !== $recipient) { + throw new ProviderException('Recipient does not match'); + } + + // We can just delete user and link shares + $this->delete($share); + } else { + throw new ProviderException('Invalid shareType'); + } + } + + /** + * @inheritdoc + */ + public function move(\OCP\Share\IShare $share, $recipient) { + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { + // Just update the target + $qb = $this->dbConn->getQueryBuilder(); + $qb->update('share') + ->set('file_target', $qb->createNamedParameter($share->getTarget())) + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) + ->execute(); + + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { + + // Check if there is a usergroup share + $qb = $this->dbConn->getQueryBuilder(); + $stmt = $qb->select('id') + ->from('share') + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))) + ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient))) + ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) + ->andWhere($qb->expr()->orX( + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) + )) + ->setMaxResults(1) + ->execute(); + + $data = $stmt->fetch(); + $stmt->closeCursor(); + + if ($data === false) { + // No usergroup share yet. Create one. + $qb = $this->dbConn->getQueryBuilder(); + $qb->insert('share') + ->values([ + 'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP), + 'share_with' => $qb->createNamedParameter($recipient), + 'uid_owner' => $qb->createNamedParameter($share->getShareOwner()), + 'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()), + 'parent' => $qb->createNamedParameter($share->getId()), + 'item_type' => $qb->createNamedParameter($share->getNode() instanceof File ? 'file' : 'folder'), + 'item_source' => $qb->createNamedParameter($share->getNode()->getId()), + 'file_source' => $qb->createNamedParameter($share->getNode()->getId()), + 'file_target' => $qb->createNamedParameter($share->getTarget()), + 'permissions' => $qb->createNamedParameter($share->getPermissions()), + 'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()), + ])->execute(); + } else { + // Already a usergroup share. Update it. + $qb = $this->dbConn->getQueryBuilder(); + $qb->update('share') + ->set('file_target', $qb->createNamedParameter($share->getTarget())) + ->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id']))) + ->execute(); + } + } + + return $share; + } + + /** + * @inheritdoc + */ + public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) { + $qb = $this->dbConn->getQueryBuilder(); + $qb->select('*') + ->from('share') + ->andWhere($qb->expr()->orX( + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) + )); + + $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType))); + + /** + * Reshares for this user are shares where they are the owner. + */ + if ($reshares === false) { + //Special case for old shares created via the web UI + $or1 = $qb->expr()->andX( + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), + $qb->expr()->isNull('uid_initiator') + ); + + $qb->andWhere( + $qb->expr()->orX( + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)), + $or1 + ) + ); + } else { + $qb->andWhere( + $qb->expr()->orX( + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) + ) + ); + } + + if ($node !== null) { + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); + } + + if ($limit !== -1) { + $qb->setMaxResults($limit); + } + + $qb->setFirstResult($offset); + $qb->orderBy('id'); + + $cursor = $qb->execute(); + $shares = []; + while($data = $cursor->fetch()) { + $shares[] = $this->createShare($data); + } + $cursor->closeCursor(); + + return $shares; + } + + /** + * @inheritdoc + */ + public function getShareById($id, $recipientId = null) { + $qb = $this->dbConn->getQueryBuilder(); + + $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) + ->andWhere( + $qb->expr()->in( + 'share_type', + $qb->createNamedParameter([ + \OCP\Share::SHARE_TYPE_USER, + \OCP\Share::SHARE_TYPE_GROUP, + \OCP\Share::SHARE_TYPE_LINK, + ], IQueryBuilder::PARAM_INT_ARRAY) + ) + ) + ->andWhere($qb->expr()->orX( + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) + )); + + $cursor = $qb->execute(); + $data = $cursor->fetch(); + $cursor->closeCursor(); + + if ($data === false) { + throw new ShareNotFound(); + } + + try { + $share = $this->createShare($data); + } catch (InvalidShare $e) { + throw new ShareNotFound(); + } + + // If the recipient is set for a group share resolve to that user + if ($recipientId !== null && $share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { + $share = $this->resolveGroupShare($share, $recipientId); + } + + return $share; + } + + /** + * Get shares for a given path + * + * @param \OCP\Files\Node $path + * @return \OCP\Share\IShare[] + */ + public function getSharesByPath(Node $path) { + $qb = $this->dbConn->getQueryBuilder(); + + $cursor = $qb->select('*') + ->from('share') + ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) + ->andWhere( + $qb->expr()->orX( + $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)), + $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)) + ) + ) + ->andWhere($qb->expr()->orX( + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) + )) + ->execute(); + + $shares = []; + while($data = $cursor->fetch()) { + $shares[] = $this->createShare($data); + } + $cursor->closeCursor(); + + return $shares; + } + + /** + * @inheritdoc + */ + public function getSharedWith($userId, $shareType, $node, $limit, $offset) { + /** @var Share[] $shares */ + $shares = []; + + if ($shareType === \OCP\Share::SHARE_TYPE_USER) { + //Get shares directly with this user + $qb = $this->dbConn->getQueryBuilder(); + $qb->select('*') + ->from('share'); + + // Order by id + $qb->orderBy('id'); + + // Set limit and offset + if ($limit !== -1) { + $qb->setMaxResults($limit); + } + $qb->setFirstResult($offset); + + $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER))) + ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))) + ->andWhere($qb->expr()->orX( + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) + )); + + // Filter by node if provided + if ($node !== null) { + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); + } + + $cursor = $qb->execute(); + + while($data = $cursor->fetch()) { + $shares[] = $this->createShare($data); + } + $cursor->closeCursor(); + + } else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) { + $user = $this->userManager->get($userId); + $allGroups = $this->groupManager->getUserGroups($user); + + /** @var Share[] $shares2 */ + $shares2 = []; + + $start = 0; + while(true) { + $groups = array_slice($allGroups, $start, 100); + $start += 100; + + if ($groups === []) { + break; + } + + $qb = $this->dbConn->getQueryBuilder(); + $qb->select('*') + ->from('share') + ->orderBy('id') + ->setFirstResult(0); + + if ($limit !== -1) { + $qb->setMaxResults($limit - count($shares)); + } + + // Filter by node if provided + if ($node !== null) { + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); + } + + $groups = array_map(function(IGroup $group) { return $group->getGID(); }, $groups); + + $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP))) + ->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter( + $groups, + IQueryBuilder::PARAM_STR_ARRAY + ))) + ->andWhere($qb->expr()->orX( + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) + )); + + $cursor = $qb->execute(); + while($data = $cursor->fetch()) { + if ($offset > 0) { + $offset--; + continue; + } + $shares2[] = $this->createShare($data); + } + $cursor->closeCursor(); + } + + /* + * Resolve all group shares to user specific shares + * TODO: Optmize this! + */ + foreach($shares2 as $share) { + $shares[] = $this->resolveGroupShare($share, $userId); + } + } else { + throw new BackendError('Invalid backend'); + } + + + return $shares; + } + + /** + * Get a share by token + * + * @param string $token + * @return \OCP\Share\IShare + * @throws ShareNotFound + */ + public function getShareByToken($token) { + $qb = $this->dbConn->getQueryBuilder(); + + $cursor = $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK))) + ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) + ->andWhere($qb->expr()->orX( + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) + )) + ->execute(); + + $data = $cursor->fetch(); + + if ($data === false) { + throw new ShareNotFound(); + } + + try { + $share = $this->createShare($data); + } catch (InvalidShare $e) { + throw new ShareNotFound(); + } + + return $share; + } + + /** + * Create a share object from an database row + * + * @param mixed[] $data + * @return \OCP\Share\IShare + * @throws InvalidShare + */ + private function createShare($data) { + $share = new Share($this->rootFolder); + $share->setId((int)$data['id']) + ->setShareType((int)$data['share_type']) + ->setPermissions((int)$data['permissions']) + ->setTarget($data['file_target']) + ->setMailSend((bool)$data['mail_send']); + + $shareTime = new \DateTime(); + $shareTime->setTimestamp((int)$data['stime']); + $share->setShareTime($shareTime); + + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { + $share->setSharedWith($data['share_with']); + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { + $share->setSharedWith($data['share_with']); + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { + $share->setPassword($data['share_with']); + $share->setToken($data['token']); + } + + if ($data['uid_initiator'] === null) { + //OLD SHARE + $share->setSharedBy($data['uid_owner']); + $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']); + + $owner = $path->getOwner(); + $share->setShareOwner($owner->getUID()); + } else { + //New share! + $share->setSharedBy($data['uid_initiator']); + $share->setShareOwner($data['uid_owner']); + } + + $share->setNodeId((int)$data['file_source']); + $share->setNodeType($data['item_type']); + + if ($data['expiration'] !== null) { + $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']); + $share->setExpirationDate($expiration); + } + + $share->setProviderId($this->identifier()); + + return $share; + } + + /** + * Get the node with file $id for $user + * + * @param string $user The userId + * @param int $id + * @return \OCP\Files\File|\OCP\Files\Folder + * @throws InvalidShare + */ + private function getNode($user, $id) { + try { + $userFolder = $this->rootFolder->getUserFolder($user); + } catch (NotFoundException $e) { + throw new InvalidShare(); + } + + $nodes = $userFolder->getById($id); + + if (empty($nodes)) { + throw new InvalidShare(); + } + + return $nodes[0]; + } + + /** + * Resolve a group share to a user specific share + * Thus if the user moved their group share make sure this is properly reflected here. + * + * @param \OCP\Share\IShare $share + * @param string $userId + * @return Share Returns the updated share if one was found else return the original share. + */ + private function resolveGroupShare(\OCP\Share\IShare $share, $userId) { + $qb = $this->dbConn->getQueryBuilder(); + + $stmt = $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))) + ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))) + ->andWhere($qb->expr()->orX( + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) + )) + ->setMaxResults(1) + ->execute(); + + $data = $stmt->fetch(); + $stmt->closeCursor(); + + if ($data !== false) { + $share->setPermissions((int)$data['permissions']); + $share->setTarget($data['file_target']); + } + + return $share; + } + +} diff --git a/lib/private/Share20/Exception/BackendError.php b/lib/private/Share20/Exception/BackendError.php new file mode 100644 index 00000000000..f15dea4a243 --- /dev/null +++ b/lib/private/Share20/Exception/BackendError.php @@ -0,0 +1,25 @@ +<?php +/** + * @author Roeland Jago Douma <rullzer@owncloud.com> + * + * @copyright Copyright (c) 2016, 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\Share20\Exception; + +class BackendError extends \Exception { + +} diff --git a/lib/private/Share20/Exception/InvalidShare.php b/lib/private/Share20/Exception/InvalidShare.php new file mode 100644 index 00000000000..c176e4424ba --- /dev/null +++ b/lib/private/Share20/Exception/InvalidShare.php @@ -0,0 +1,25 @@ +<?php +/** + * @author Roeland Jago Douma <rullzer@owncloud.com> + * + * @copyright Copyright (c) 2016, 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\Share20\Exception; + +class InvalidShare extends \Exception { + +} diff --git a/lib/private/Share20/Exception/ProviderException.php b/lib/private/Share20/Exception/ProviderException.php new file mode 100644 index 00000000000..a14d5266581 --- /dev/null +++ b/lib/private/Share20/Exception/ProviderException.php @@ -0,0 +1,27 @@ +<?php +/** + * @author Roeland Jago Douma <rullzer@owncloud.com> + * + * @copyright Copyright (c) 2016, 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\Share20\Exception; + + +class ProviderException extends \Exception { + +} + diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php new file mode 100644 index 00000000000..95662dc0b60 --- /dev/null +++ b/lib/private/Share20/Manager.php @@ -0,0 +1,1150 @@ +<?php +/** + * @author Arthur Schiwon <blizzz@owncloud.com> + * @author Joas Schilling <nickvergessen@owncloud.com> + * @author Roeland Jago Douma <rullzer@owncloud.com> + * + * @copyright Copyright (c) 2016, 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\Share20; + +use OCP\Files\IRootFolder; +use OCP\Files\NotFoundException; +use OCP\IUserManager; +use OCP\Share\IManager; +use OCP\Share\IProviderFactory; +use OC\Share20\Exception\BackendError; +use OCP\IConfig; +use OCP\IL10N; +use OCP\ILogger; +use OCP\Security\ISecureRandom; +use OCP\Security\IHasher; +use OCP\Files\Mount\IMountManager; +use OCP\IGroupManager; +use OCP\Files\File; +use OCP\Files\Folder; + +use OCP\Share\Exceptions\ShareNotFound; +use OCP\Share\Exceptions\GenericShareException; + +/** + * This class is the communication hub for all sharing related operations. + */ +class Manager implements IManager { + + /** @var IProviderFactory */ + private $factory; + /** @var ILogger */ + private $logger; + /** @var IConfig */ + private $config; + /** @var ISecureRandom */ + private $secureRandom; + /** @var IHasher */ + private $hasher; + /** @var IMountManager */ + private $mountManager; + /** @var IGroupManager */ + private $groupManager; + /** @var IL10N */ + private $l; + /** @var IUserManager */ + private $userManager; + /** @var IRootFolder */ + private $rootFolder; + + /** + * Manager constructor. + * + * @param ILogger $logger + * @param IConfig $config + * @param ISecureRandom $secureRandom + * @param IHasher $hasher + * @param IMountManager $mountManager + * @param IGroupManager $groupManager + * @param IL10N $l + * @param IProviderFactory $factory + * @param IUserManager $userManager + * @param IRootFolder $rootFolder + */ + public function __construct( + ILogger $logger, + IConfig $config, + ISecureRandom $secureRandom, + IHasher $hasher, + IMountManager $mountManager, + IGroupManager $groupManager, + IL10N $l, + IProviderFactory $factory, + IUserManager $userManager, + IRootFolder $rootFolder + ) { + $this->logger = $logger; + $this->config = $config; + $this->secureRandom = $secureRandom; + $this->hasher = $hasher; + $this->mountManager = $mountManager; + $this->groupManager = $groupManager; + $this->l = $l; + $this->factory = $factory; + $this->userManager = $userManager; + $this->rootFolder = $rootFolder; + } + + /** + * Convert from a full share id to a tuple (providerId, shareId) + * + * @param string $id + * @return string[] + */ + private function splitFullId($id) { + return explode(':', $id, 2); + } + + /** + * Verify if a password meets all requirements + * + * @param string $password + * @throws \Exception + */ + protected function verifyPassword($password) { + if ($password === null) { + // No password is set, check if this is allowed. + if ($this->shareApiLinkEnforcePassword()) { + throw new \InvalidArgumentException('Passwords are enforced for link shares'); + } + + return; + } + + // Let others verify the password + $accepted = true; + $message = ''; + \OCP\Util::emitHook('\OC\Share', 'verifyPassword', [ + 'password' => $password, + 'accepted' => &$accepted, + 'message' => &$message + ]); + + if (!$accepted) { + throw new \Exception($message); + } + } + + /** + * Check for generic requirements before creating a share + * + * @param \OCP\Share\IShare $share + * @throws \InvalidArgumentException + * @throws GenericShareException + */ + protected function generalCreateChecks(\OCP\Share\IShare $share) { + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { + // We expect a valid user as sharedWith for user shares + if (!$this->userManager->userExists($share->getSharedWith())) { + throw new \InvalidArgumentException('SharedWith is not a valid user'); + } + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { + // We expect a valid group as sharedWith for group shares + if (!$this->groupManager->groupExists($share->getSharedWith())) { + throw new \InvalidArgumentException('SharedWith is not a valid group'); + } + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { + if ($share->getSharedWith() !== null) { + throw new \InvalidArgumentException('SharedWith should be empty'); + } + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) { + if ($share->getSharedWith() === null) { + throw new \InvalidArgumentException('SharedWith should not be empty'); + } + } else { + // We can't handle other types yet + throw new \InvalidArgumentException('unkown share type'); + } + + // Verify the initiator of the share is set + if ($share->getSharedBy() === null) { + throw new \InvalidArgumentException('SharedBy should be set'); + } + + // Cannot share with yourself + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && + $share->getSharedWith() === $share->getSharedBy()) { + throw new \InvalidArgumentException('Can\'t share with yourself'); + } + + // The path should be set + if ($share->getNode() === null) { + throw new \InvalidArgumentException('Path should be set'); + } + + // And it should be a file or a folder + if (!($share->getNode() instanceof \OCP\Files\File) && + !($share->getNode() instanceof \OCP\Files\Folder)) { + throw new \InvalidArgumentException('Path should be either a file or a folder'); + } + + // And you can't share your rootfolder + if ($this->rootFolder->getUserFolder($share->getSharedBy())->getPath() === $share->getNode()->getPath()) { + throw new \InvalidArgumentException('You can\'t share your root folder'); + } + + // Check if we actually have share permissions + if (!$share->getNode()->isShareable()) { + $message_t = $this->l->t('You are not allowed to share %s', [$share->getNode()->getPath()]); + throw new GenericShareException($message_t, $message_t, 404); + } + + // Permissions should be set + if ($share->getPermissions() === null) { + throw new \InvalidArgumentException('A share requires permissions'); + } + + // Check that we do not share with more permissions than we have + if ($share->getPermissions() & ~$share->getNode()->getPermissions()) { + $message_t = $this->l->t('Cannot increase permissions of %s', [$share->getNode()->getPath()]); + throw new GenericShareException($message_t, $message_t, 404); + } + + // Check that read permissions are always set + if (($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) { + throw new \InvalidArgumentException('Shares need at least read permissions'); + } + } + + /** + * Validate if the expiration date fits the system settings + * + * @param \OCP\Share\IShare $share The share to validate the expiration date of + * @return \OCP\Share\IShare The modified share object + * @throws GenericShareException + * @throws \InvalidArgumentException + * @throws \Exception + */ + protected function validateExpirationDate(\OCP\Share\IShare $share) { + + $expirationDate = $share->getExpirationDate(); + + if ($expirationDate !== null) { + //Make sure the expiration date is a date + $expirationDate->setTime(0, 0, 0); + + $date = new \DateTime(); + $date->setTime(0, 0, 0); + if ($date >= $expirationDate) { + $message = $this->l->t('Expiration date is in the past'); + throw new GenericShareException($message, $message, 404); + } + } + + // If expiredate is empty set a default one if there is a default + $fullId = null; + try { + $fullId = $share->getFullId(); + } catch (\UnexpectedValueException $e) { + // This is a new share + } + + if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) { + $expirationDate = new \DateTime(); + $expirationDate->setTime(0,0,0); + $expirationDate->add(new \DateInterval('P'.$this->shareApiLinkDefaultExpireDays().'D')); + } + + // If we enforce the expiration date check that is does not exceed + if ($this->shareApiLinkDefaultExpireDateEnforced()) { + if ($expirationDate === null) { + throw new \InvalidArgumentException('Expiration date is enforced'); + } + + $date = new \DateTime(); + $date->setTime(0, 0, 0); + $date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D')); + if ($date < $expirationDate) { + $message = $this->l->t('Cannot set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]); + throw new GenericShareException($message, $message, 404); + } + } + + $accepted = true; + $message = ''; + \OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [ + 'expirationDate' => &$expirationDate, + 'accepted' => &$accepted, + 'message' => &$message, + 'passwordSet' => $share->getPassword() !== null, + ]); + + if (!$accepted) { + throw new \Exception($message); + } + + $share->setExpirationDate($expirationDate); + + return $share; + } + + /** + * Check for pre share requirements for user shares + * + * @param \OCP\Share\IShare $share + * @throws \Exception + */ + protected function userCreateChecks(\OCP\Share\IShare $share) { + // Check if we can share with group members only + if ($this->shareWithGroupMembersOnly()) { + $sharedBy = $this->userManager->get($share->getSharedBy()); + $sharedWith = $this->userManager->get($share->getSharedWith()); + // Verify we can share with this user + $groups = array_intersect( + $this->groupManager->getUserGroupIds($sharedBy), + $this->groupManager->getUserGroupIds($sharedWith) + ); + if (empty($groups)) { + throw new \Exception('Only sharing with group members is allowed'); + } + } + + /* + * TODO: Could be costly, fix + * + * Also this is not what we want in the future.. then we want to squash identical shares. + */ + $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_USER); + $existingShares = $provider->getSharesByPath($share->getNode()); + foreach($existingShares as $existingShare) { + // Ignore if it is the same share + try { + if ($existingShare->getFullId() === $share->getFullId()) { + continue; + } + } catch (\UnexpectedValueException $e) { + //Shares are not identical + } + + // Identical share already existst + if ($existingShare->getSharedWith() === $share->getSharedWith()) { + throw new \Exception('Path already shared with this user'); + } + + // The share is already shared with this user via a group share + if ($existingShare->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { + $group = $this->groupManager->get($existingShare->getSharedWith()); + $user = $this->userManager->get($share->getSharedWith()); + + if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) { + throw new \Exception('Path already shared with this user'); + } + } + } + } + + /** + * Check for pre share requirements for group shares + * + * @param \OCP\Share\IShare $share + * @throws \Exception + */ + protected function groupCreateChecks(\OCP\Share\IShare $share) { + // Verify if the user can share with this group + if ($this->shareWithGroupMembersOnly()) { + $sharedBy = $this->userManager->get($share->getSharedBy()); + $sharedWith = $this->groupManager->get($share->getSharedWith()); + if (!$sharedWith->inGroup($sharedBy)) { + throw new \Exception('Only sharing within your own groups is allowed'); + } + } + + /* + * TODO: Could be costly, fix + * + * Also this is not what we want in the future.. then we want to squash identical shares. + */ + $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP); + $existingShares = $provider->getSharesByPath($share->getNode()); + foreach($existingShares as $existingShare) { + try { + if ($existingShare->getFullId() === $share->getFullId()) { + continue; + } + } catch (\UnexpectedValueException $e) { + //It is a new share so just continue + } + + if ($existingShare->getSharedWith() === $share->getSharedWith()) { + throw new \Exception('Path already shared with this group'); + } + } + } + + /** + * Check for pre share requirements for link shares + * + * @param \OCP\Share\IShare $share + * @throws \Exception + */ + protected function linkCreateChecks(\OCP\Share\IShare $share) { + // Are link shares allowed? + if (!$this->shareApiAllowLinks()) { + throw new \Exception('Link sharing not allowed'); + } + + // Link shares by definition can't have share permissions + if ($share->getPermissions() & \OCP\Constants::PERMISSION_SHARE) { + throw new \InvalidArgumentException('Link shares can\'t have reshare permissions'); + } + + // We don't allow deletion on link shares + if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) { + throw new \InvalidArgumentException('Link shares can\'t have delete permissions'); + } + + // Check if public upload is allowed + if (!$this->shareApiLinkAllowPublicUpload() && + ($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE))) { + throw new \InvalidArgumentException('Public upload not allowed'); + } + } + + /** + * To make sure we don't get invisible link shares we set the parent + * of a link if it is a reshare. This is a quick word around + * until we can properly display multiple link shares in the UI + * + * See: https://github.com/owncloud/core/issues/22295 + * + * FIXME: Remove once multiple link shares can be properly displayed + * + * @param \OCP\Share\IShare $share + */ + protected function setLinkParent(\OCP\Share\IShare $share) { + + // No sense in checking if the method is not there. + if (method_exists($share, 'setParent')) { + $storage = $share->getNode()->getStorage(); + if ($storage->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) { + $share->setParent($storage->getShareId()); + } + }; + } + + /** + * @param File|Folder $path + */ + protected function pathCreateChecks($path) { + // Make sure that we do not share a path that contains a shared mountpoint + if ($path instanceof \OCP\Files\Folder) { + $mounts = $this->mountManager->findIn($path->getPath()); + foreach($mounts as $mount) { + if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) { + throw new \InvalidArgumentException('Path contains files shared with you'); + } + } + } + } + + /** + * Check if the user that is sharing can actually share + * + * @param \OCP\Share\IShare $share + * @throws \Exception + */ + protected function canShare(\OCP\Share\IShare $share) { + if (!$this->shareApiEnabled()) { + throw new \Exception('The share API is disabled'); + } + + if ($this->sharingDisabledForUser($share->getSharedBy())) { + throw new \Exception('You are not allowed to share'); + } + } + + /** + * Share a path + * + * @param \OCP\Share\IShare $share + * @return Share The share object + * @throws \Exception + * + * TODO: handle link share permissions or check them + */ + public function createShare(\OCP\Share\IShare $share) { + $this->canShare($share); + + $this->generalCreateChecks($share); + + //Verify share type + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { + $this->userCreateChecks($share); + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { + $this->groupCreateChecks($share); + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { + $this->linkCreateChecks($share); + $this->setLinkParent($share); + + /* + * For now ignore a set token. + */ + $share->setToken( + $this->secureRandom->generate( + \OC\Share\Constants::TOKEN_LENGTH, + \OCP\Security\ISecureRandom::CHAR_LOWER. + \OCP\Security\ISecureRandom::CHAR_UPPER. + \OCP\Security\ISecureRandom::CHAR_DIGITS + ) + ); + + //Verify the expiration date + $this->validateExpirationDate($share); + + //Verify the password + $this->verifyPassword($share->getPassword()); + + // If a password is set. Hash it! + if ($share->getPassword() !== null) { + $share->setPassword($this->hasher->hash($share->getPassword())); + } + } + + // Verify if there are any issues with the path + $this->pathCreateChecks($share->getNode()); + + /* + * On creation of a share the owner is always the owner of the path + * Except for mounted federated shares. + */ + $storage = $share->getNode()->getStorage(); + if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) { + $parent = $share->getNode()->getParent(); + while($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) { + $parent = $parent->getParent(); + } + $share->setShareOwner($parent->getOwner()->getUID()); + } else { + $share->setShareOwner($share->getNode()->getOwner()->getUID()); + } + + // Cannot share with the owner + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && + $share->getSharedWith() === $share->getShareOwner()) { + throw new \InvalidArgumentException('Can\'t share with the share owner'); + } + + // Generate the target + $target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getNode()->getName(); + $target = \OC\Files\Filesystem::normalizePath($target); + $share->setTarget($target); + + // Pre share hook + $run = true; + $error = ''; + $preHookData = [ + 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder', + 'itemSource' => $share->getNode()->getId(), + 'shareType' => $share->getShareType(), + 'uidOwner' => $share->getSharedBy(), + 'permissions' => $share->getPermissions(), + 'fileSource' => $share->getNode()->getId(), + 'expiration' => $share->getExpirationDate(), + 'token' => $share->getToken(), + 'itemTarget' => $share->getTarget(), + 'shareWith' => $share->getSharedWith(), + 'run' => &$run, + 'error' => &$error, + ]; + \OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData); + + if ($run === false) { + throw new \Exception($error); + } + + $provider = $this->factory->getProviderForType($share->getShareType()); + $share = $provider->create($share); + + // Post share hook + $postHookData = [ + 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder', + 'itemSource' => $share->getNode()->getId(), + 'shareType' => $share->getShareType(), + 'uidOwner' => $share->getSharedBy(), + 'permissions' => $share->getPermissions(), + 'fileSource' => $share->getNode()->getId(), + 'expiration' => $share->getExpirationDate(), + 'token' => $share->getToken(), + 'id' => $share->getId(), + 'shareWith' => $share->getSharedWith(), + 'itemTarget' => $share->getTarget(), + 'fileTarget' => $share->getTarget(), + ]; + + \OC_Hook::emit('OCP\Share', 'post_shared', $postHookData); + + return $share; + } + + /** + * Update a share + * + * @param \OCP\Share\IShare $share + * @return \OCP\Share\IShare The share object + * @throws \InvalidArgumentException + */ + public function updateShare(\OCP\Share\IShare $share) { + $expirationDateUpdated = false; + + $this->canShare($share); + + try { + $originalShare = $this->getShareById($share->getFullId()); + } catch (\UnexpectedValueException $e) { + throw new \InvalidArgumentException('Share does not have a full id'); + } + + // We can't change the share type! + if ($share->getShareType() !== $originalShare->getShareType()) { + throw new \InvalidArgumentException('Can\'t change share type'); + } + + // We can only change the recipient on user shares + if ($share->getSharedWith() !== $originalShare->getSharedWith() && + $share->getShareType() !== \OCP\Share::SHARE_TYPE_USER) { + throw new \InvalidArgumentException('Can only update recipient on user shares'); + } + + // Cannot share with the owner + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && + $share->getSharedWith() === $share->getShareOwner()) { + throw new \InvalidArgumentException('Can\'t share with the share owner'); + } + + $this->generalCreateChecks($share); + + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { + $this->userCreateChecks($share); + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { + $this->groupCreateChecks($share); + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { + $this->linkCreateChecks($share); + + // Password updated. + if ($share->getPassword() !== $originalShare->getPassword()) { + //Verify the password + $this->verifyPassword($share->getPassword()); + + // If a password is set. Hash it! + if ($share->getPassword() !== null) { + $share->setPassword($this->hasher->hash($share->getPassword())); + } + } + + if ($share->getExpirationDate() != $originalShare->getExpirationDate()) { + //Verify the expiration date + $this->validateExpirationDate($share); + $expirationDateUpdated = true; + } + } + + $this->pathCreateChecks($share->getNode()); + + // Now update the share! + $provider = $this->factory->getProviderForType($share->getShareType()); + $share = $provider->update($share); + + if ($expirationDateUpdated === true) { + \OC_Hook::emit('OCP\Share', 'post_set_expiration_date', [ + 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder', + 'itemSource' => $share->getNode()->getId(), + 'date' => $share->getExpirationDate(), + 'uidOwner' => $share->getSharedBy(), + ]); + } + + if ($share->getPassword() !== $originalShare->getPassword()) { + \OC_Hook::emit('OCP\Share', 'post_update_password', [ + 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder', + 'itemSource' => $share->getNode()->getId(), + 'uidOwner' => $share->getSharedBy(), + 'token' => $share->getToken(), + 'disabled' => is_null($share->getPassword()), + ]); + } + + if ($share->getPermissions() !== $originalShare->getPermissions()) { + $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner()); + \OC_Hook::emit('OCP\Share', 'post_update_permissions', array( + 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder', + 'itemSource' => $share->getNode()->getId(), + 'shareType' => $share->getShareType(), + 'shareWith' => $share->getSharedWith(), + 'uidOwner' => $share->getSharedBy(), + 'permissions' => $share->getPermissions(), + 'path' => $userFolder->getRelativePath($share->getNode()->getPath()), + )); + } + + return $share; + } + + /** + * Delete all the children of this share + * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in + * + * @param \OCP\Share\IShare $share + * @return \OCP\Share\IShare[] List of deleted shares + */ + protected function deleteChildren(\OCP\Share\IShare $share) { + $deletedShares = []; + + $provider = $this->factory->getProviderForType($share->getShareType()); + + foreach ($provider->getChildren($share) as $child) { + $deletedChildren = $this->deleteChildren($child); + $deletedShares = array_merge($deletedShares, $deletedChildren); + + $provider->delete($child); + $deletedShares[] = $child; + } + + return $deletedShares; + } + + /** + * Delete a share + * + * @param \OCP\Share\IShare $share + * @throws ShareNotFound + * @throws \InvalidArgumentException + */ + public function deleteShare(\OCP\Share\IShare $share) { + + try { + $share->getFullId(); + } catch (\UnexpectedValueException $e) { + throw new \InvalidArgumentException('Share does not have a full id'); + } + + $formatHookParams = function(\OCP\Share\IShare $share) { + // Prepare hook + $shareType = $share->getShareType(); + $sharedWith = ''; + if ($shareType === \OCP\Share::SHARE_TYPE_USER) { + $sharedWith = $share->getSharedWith(); + } else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) { + $sharedWith = $share->getSharedWith(); + } else if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) { + $sharedWith = $share->getSharedWith(); + } + + $hookParams = [ + 'id' => $share->getId(), + 'itemType' => $share->getNodeType(), + 'itemSource' => $share->getNodeId(), + 'shareType' => $shareType, + 'shareWith' => $sharedWith, + 'itemparent' => method_exists($share, 'getParent') ? $share->getParent() : '', + 'uidOwner' => $share->getSharedBy(), + 'fileSource' => $share->getNodeId(), + 'fileTarget' => $share->getTarget() + ]; + return $hookParams; + }; + + $hookParams = $formatHookParams($share); + + // Emit pre-hook + \OC_Hook::emit('OCP\Share', 'pre_unshare', $hookParams); + + // Get all children and delete them as well + $deletedShares = $this->deleteChildren($share); + + // Do the actual delete + $provider = $this->factory->getProviderForType($share->getShareType()); + $provider->delete($share); + + // All the deleted shares caused by this delete + $deletedShares[] = $share; + + //Format hook info + $formattedDeletedShares = array_map(function($share) use ($formatHookParams) { + return $formatHookParams($share); + }, $deletedShares); + + $hookParams['deletedShares'] = $formattedDeletedShares; + + // Emit post hook + \OC_Hook::emit('OCP\Share', 'post_unshare', $hookParams); + } + + + /** + * Unshare a file as the recipient. + * This can be different from a regular delete for example when one of + * the users in a groups deletes that share. But the provider should + * handle this. + * + * @param \OCP\Share\IShare $share + * @param string $recipientId + */ + public function deleteFromSelf(\OCP\Share\IShare $share, $recipientId) { + list($providerId, ) = $this->splitFullId($share->getId()); + $provider = $this->factory->getProvider($providerId); + + $provider->deleteFromSelf($share, $recipientId); + } + + /** + * @inheritdoc + */ + public function moveShare(\OCP\Share\IShare $share, $recipientId) { + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { + throw new \InvalidArgumentException('Can\'t change target of link share'); + } + + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() !== $recipientId) { + throw new \InvalidArgumentException('Invalid recipient'); + } + + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { + $sharedWith = $this->groupManager->get($share->getSharedWith()); + $recipient = $this->userManager->get($recipientId); + if (!$sharedWith->inGroup($recipient)) { + throw new \InvalidArgumentException('Invalid recipient'); + } + } + + list($providerId, ) = $this->splitFullId($share->getId()); + $provider = $this->factory->getProvider($providerId); + + $provider->move($share, $recipientId); + } + + /** + * @inheritdoc + */ + public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) { + if ($path !== null && + !($path instanceof \OCP\Files\File) && + !($path instanceof \OCP\Files\Folder)) { + throw new \InvalidArgumentException('invalid path'); + } + + $provider = $this->factory->getProviderForType($shareType); + + $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset); + + /* + * Work around so we don't return expired shares but still follow + * proper pagination. + */ + if ($shareType === \OCP\Share::SHARE_TYPE_LINK) { + $shares2 = []; + $today = new \DateTime(); + + while(true) { + $added = 0; + foreach ($shares as $share) { + // Check if the share is expired and if so delete it + if ($share->getExpirationDate() !== null && + $share->getExpirationDate() <= $today + ) { + try { + $this->deleteShare($share); + } catch (NotFoundException $e) { + //Ignore since this basically means the share is deleted + } + continue; + } + $added++; + $shares2[] = $share; + + if (count($shares2) === $limit) { + break; + } + } + + if (count($shares2) === $limit) { + break; + } + + // If there was no limit on the select we are done + if ($limit === -1) { + break; + } + + $offset += $added; + + // Fetch again $limit shares + $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset); + + // No more shares means we are done + if (empty($shares)) { + break; + } + } + + $shares = $shares2; + } + + return $shares; + } + + /** + * @inheritdoc + */ + public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) { + $provider = $this->factory->getProviderForType($shareType); + + return $provider->getSharedWith($userId, $shareType, $node, $limit, $offset); + } + + /** + * @inheritdoc + */ + public function getShareById($id, $recipient = null) { + if ($id === null) { + throw new ShareNotFound(); + } + + list($providerId, $id) = $this->splitFullId($id); + $provider = $this->factory->getProvider($providerId); + + $share = $provider->getShareById($id, $recipient); + + // Validate link shares expiration date + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK && + $share->getExpirationDate() !== null && + $share->getExpirationDate() <= new \DateTime()) { + $this->deleteShare($share); + throw new ShareNotFound(); + } + + return $share; + } + + /** + * Get all the shares for a given path + * + * @param \OCP\Files\Node $path + * @param int $page + * @param int $perPage + * + * @return Share[] + */ + public function getSharesByPath(\OCP\Files\Node $path, $page=0, $perPage=50) { + } + + /** + * Get the share by token possible with password + * + * @param string $token + * @return Share + * + * @throws ShareNotFound + */ + public function getShareByToken($token) { + $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_LINK); + + $share = $provider->getShareByToken($token); + + if ($share->getExpirationDate() !== null && + $share->getExpirationDate() <= new \DateTime()) { + $this->deleteShare($share); + throw new ShareNotFound(); + } + + return $share; + } + + /** + * Verify the password of a public share + * + * @param \OCP\Share\IShare $share + * @param string $password + * @return bool + */ + public function checkPassword(\OCP\Share\IShare $share, $password) { + if ($share->getShareType() !== \OCP\Share::SHARE_TYPE_LINK) { + //TODO maybe exception? + return false; + } + + if ($password === null || $share->getPassword() === null) { + return false; + } + + $newHash = ''; + if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) { + return false; + } + + if (!empty($newHash)) { + $share->setPassword($newHash); + $provider = $this->factory->getProviderForType($share->getShareType()); + $provider->update($share); + } + + return true; + } + + /** + * Get access list to a path. This means + * all the users and groups that can access a given path. + * + * Consider: + * -root + * |-folder1 + * |-folder2 + * |-fileA + * + * fileA is shared with user1 + * folder2 is shared with group2 + * folder1 is shared with user2 + * + * Then the access list will to '/folder1/folder2/fileA' is: + * [ + * 'users' => ['user1', 'user2'], + * 'groups' => ['group2'] + * ] + * + * This is required for encryption + * + * @param \OCP\Files\Node $path + */ + public function getAccessList(\OCP\Files\Node $path) { + } + + /** + * Create a new share + * @return \OCP\Share\IShare; + */ + public function newShare() { + return new \OC\Share20\Share($this->rootFolder); + } + + /** + * Is the share API enabled + * + * @return bool + */ + public function shareApiEnabled() { + return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes'; + } + + /** + * Is public link sharing enabled + * + * @return bool + */ + public function shareApiAllowLinks() { + return $this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes'; + } + + /** + * Is password on public link requires + * + * @return bool + */ + public function shareApiLinkEnforcePassword() { + return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes'; + } + + /** + * Is default expire date enabled + * + * @return bool + */ + public function shareApiLinkDefaultExpireDate() { + return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes'; + } + + /** + * Is default expire date enforced + *` + * @return bool + */ + public function shareApiLinkDefaultExpireDateEnforced() { + return $this->shareApiLinkDefaultExpireDate() && + $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes'; + } + + /** + * Number of default expire days + *shareApiLinkAllowPublicUpload + * @return int + */ + public function shareApiLinkDefaultExpireDays() { + return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7'); + } + + /** + * Allow public upload on link shares + * + * @return bool + */ + public function shareApiLinkAllowPublicUpload() { + return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes'; + } + + /** + * check if user can only share with group members + * @return bool + */ + public function shareWithGroupMembersOnly() { + return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes'; + } + + + /** + * Copied from \OC_Util::isSharingDisabledForUser + * + * TODO: Deprecate fuction from OC_Util + * + * @param string $userId + * @return bool + */ + public function sharingDisabledForUser($userId) { + if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') { + $groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', ''); + $excludedGroups = json_decode($groupsList); + if (is_null($excludedGroups)) { + $excludedGroups = explode(',', $groupsList); + $newValue = json_encode($excludedGroups); + $this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue); + } + $user = $this->userManager->get($userId); + $usersGroups = $this->groupManager->getUserGroupIds($user); + if (!empty($usersGroups)) { + $remainingGroups = array_diff($usersGroups, $excludedGroups); + // if the user is only in groups which are disabled for sharing then + // sharing is also disabled for the user + if (empty($remainingGroups)) { + return true; + } + } + } + return false; + } + + /** + * @inheritdoc + */ + public function outgoingServer2ServerSharesAllowed() { + return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes'; + } + +} diff --git a/lib/private/Share20/ProviderFactory.php b/lib/private/Share20/ProviderFactory.php new file mode 100644 index 00000000000..4cb1ac71a88 --- /dev/null +++ b/lib/private/Share20/ProviderFactory.php @@ -0,0 +1,163 @@ +<?php +/** + * @author Lukas Reschke <lukas@owncloud.com> + * @author Roeland Jago Douma <rullzer@owncloud.com> + * + * @copyright Copyright (c) 2016, 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\Share20; + +use OCA\FederatedFileSharing\AddressHandler; +use OCA\FederatedFileSharing\DiscoveryManager; +use OCA\FederatedFileSharing\FederatedShareProvider; +use OCA\FederatedFileSharing\Notifications; +use OCA\FederatedFileSharing\TokenHandler; +use OCP\Share\IProviderFactory; +use OC\Share20\Exception\ProviderException; +use OCP\IServerContainer; + +/** + * Class ProviderFactory + * + * @package OC\Share20 + */ +class ProviderFactory implements IProviderFactory { + + /** @var IServerContainer */ + private $serverContainer; + /** @var DefaultShareProvider */ + private $defaultProvider = null; + /** @var FederatedShareProvider */ + private $federatedProvider = null; + + /** + * IProviderFactory constructor. + * @param IServerContainer $serverContainer + */ + public function __construct(IServerContainer $serverContainer) { + $this->serverContainer = $serverContainer; + } + + /** + * Create the default share provider. + * + * @return DefaultShareProvider + */ + protected function defaultShareProvider() { + if ($this->defaultProvider === null) { + $this->defaultProvider = new DefaultShareProvider( + $this->serverContainer->getDatabaseConnection(), + $this->serverContainer->getUserManager(), + $this->serverContainer->getGroupManager(), + $this->serverContainer->getRootFolder() + ); + } + + return $this->defaultProvider; + } + + /** + * Create the federated share provider + * + * @return FederatedShareProvider + */ + protected function federatedShareProvider() { + if ($this->federatedProvider === null) { + /* + * Check if the app is enabled + */ + $appManager = $this->serverContainer->getAppManager(); + if (!$appManager->isEnabledForUser('federatedfilesharing')) { + return null; + } + + /* + * TODO: add factory to federated sharing app + */ + $l = $this->serverContainer->getL10N('federatedfilessharing'); + $addressHandler = new AddressHandler( + $this->serverContainer->getURLGenerator(), + $l + ); + $discoveryManager = new DiscoveryManager( + $this->serverContainer->getMemCacheFactory(), + $this->serverContainer->getHTTPClientService() + ); + $notifications = new Notifications( + $addressHandler, + $this->serverContainer->getHTTPClientService(), + $discoveryManager + ); + $tokenHandler = new TokenHandler( + $this->serverContainer->getSecureRandom() + ); + + $this->federatedProvider = new FederatedShareProvider( + $this->serverContainer->getDatabaseConnection(), + $addressHandler, + $notifications, + $tokenHandler, + $l, + $this->serverContainer->getLogger(), + $this->serverContainer->getRootFolder() + ); + } + + return $this->federatedProvider; + } + + /** + * @inheritdoc + */ + public function getProvider($id) { + $provider = null; + if ($id === 'ocinternal') { + $provider = $this->defaultShareProvider(); + } else if ($id === 'ocFederatedSharing') { + $provider = $this->federatedShareProvider(); + } + + if ($provider === null) { + throw new ProviderException('No provider with id .' . $id . ' found.'); + } + + return $provider; + } + + /** + * @inheritdoc + */ + public function getProviderForType($shareType) { + $provider = null; + + //FIXME we should not report type 2 + if ($shareType === \OCP\Share::SHARE_TYPE_USER || + $shareType === 2 || + $shareType === \OCP\Share::SHARE_TYPE_GROUP || + $shareType === \OCP\Share::SHARE_TYPE_LINK) { + $provider = $this->defaultShareProvider(); + } else if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) { + $provider = $this->federatedShareProvider(); + } + + if ($provider === null) { + throw new ProviderException('No share provider for share type ' . $shareType); + } + + return $provider; + } +} diff --git a/lib/private/Share20/Share.php b/lib/private/Share20/Share.php new file mode 100644 index 00000000000..6edd0e6886a --- /dev/null +++ b/lib/private/Share20/Share.php @@ -0,0 +1,389 @@ +<?php +/** + * @author Roeland Jago Douma <rullzer@owncloud.com> + * + * @copyright Copyright (c) 2016, 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\Share20; + +use OCP\Files\File; +use OCP\Files\IRootFolder; +use OCP\Files\Node; +use OCP\Files\NotFoundException; +use OCP\IUser; +use OCP\IGroup; + +class Share implements \OCP\Share\IShare { + + /** @var string */ + private $id; + /** @var string */ + private $providerId; + /** @var Node */ + private $node; + /** @var int */ + private $fileId; + /** @var string */ + private $nodeType; + /** @var int */ + private $shareType; + /** @var string */ + private $sharedWith; + /** @var string */ + private $sharedBy; + /** @var string */ + private $shareOwner; + /** @var int */ + private $permissions; + /** @var \DateTime */ + private $expireDate; + /** @var string */ + private $password; + /** @var string */ + private $token; + /** @var int */ + private $parent; + /** @var string */ + private $target; + /** @var \DateTime */ + private $shareTime; + /** @var bool */ + private $mailSend; + + /** @var IRootFolder */ + private $rootFolder; + + public function __construct(IRootFolder $rootFolder) { + $this->rootFolder = $rootFolder; + } + + /** + * @inheritdoc + */ + public function setId($id) { + $this->id = $id; + return $this; + } + + /** + * @inheritdoc + */ + public function getId() { + return $this->id; + } + + /** + * @inheritdoc + */ + public function getFullId() { + if ($this->providerId === null || $this->id === null) { + throw new \UnexpectedValueException; + } + return $this->providerId . ':' . $this->id; + } + + /** + * @inheritdoc + */ + public function setProviderId($id) { + $this->providerId = $id; + return $this; + } + + /** + * @inheritdoc + */ + public function setNode(Node $node) { + $this->fileId = null; + $this->nodeType = null; + $this->node = $node; + return $this; + } + + /** + * @inheritdoc + */ + public function getNode() { + if ($this->node === null) { + + if ($this->shareOwner === null || $this->fileId === null) { + throw new NotFoundException(); + } + + $userFolder = $this->rootFolder->getUserFolder($this->shareOwner); + + $nodes = $userFolder->getById($this->fileId); + if (empty($nodes)) { + throw new NotFoundException(); + } + + $this->node = $nodes[0]; + } + + return $this->node; + } + + /** + * @inheritdoc + */ + public function setNodeId($fileId) { + $this->node = null; + $this->fileId = $fileId; + return $this; + } + + /** + * @inheritdoc + */ + public function getNodeId() { + if ($this->fileId === null) { + $this->fileId = $this->getNode()->getId(); + } + + return $this->fileId; + } + + /** + * @inheritdoc + */ + public function setNodeType($type) { + if ($type !== 'file' && $type !== 'folder') { + throw new \InvalidArgumentException(); + } + + $this->nodeType = $type; + return $this; + } + + /** + * @inheritdoc + */ + public function getNodeType() { + if ($this->nodeType === null) { + $node = $this->getNode(); + $this->nodeType = $node instanceof File ? 'file' : 'folder'; + } + + return $this->nodeType; + } + + /** + * @inheritdoc + */ + public function setShareType($shareType) { + $this->shareType = $shareType; + return $this; + } + + /** + * @inheritdoc + */ + public function getShareType() { + return $this->shareType; + } + + /** + * @inheritdoc + */ + public function setSharedWith($sharedWith) { + if (!is_string($sharedWith)) { + throw new \InvalidArgumentException(); + } + $this->sharedWith = $sharedWith; + return $this; + } + + /** + * @inheritdoc + */ + public function getSharedWith() { + return $this->sharedWith; + } + + /** + * @inheritdoc + */ + public function setPermissions($permissions) { + //TODO checkes + + $this->permissions = $permissions; + return $this; + } + + /** + * @inheritdoc + */ + public function getPermissions() { + return $this->permissions; + } + + /** + * @inheritdoc + */ + public function setExpirationDate($expireDate) { + //TODO checks + + $this->expireDate = $expireDate; + return $this; + } + + /** + * @inheritdoc + */ + public function getExpirationDate() { + return $this->expireDate; + } + + /** + * @inheritdoc + */ + public function setSharedBy($sharedBy) { + if (!is_string($sharedBy)) { + throw new \InvalidArgumentException(); + } + //TODO checks + $this->sharedBy = $sharedBy; + + return $this; + } + + /** + * @inheritdoc + */ + public function getSharedBy() { + //TODO check if set + return $this->sharedBy; + } + + /** + * @inheritdoc + */ + public function setShareOwner($shareOwner) { + if (!is_string($shareOwner)) { + throw new \InvalidArgumentException(); + } + //TODO checks + + $this->shareOwner = $shareOwner; + return $this; + } + + /** + * @inheritdoc + */ + public function getShareOwner() { + //TODO check if set + return $this->shareOwner; + } + + /** + * @inheritdoc + */ + public function setPassword($password) { + $this->password = $password; + return $this; + } + + /** + * @inheritdoc + */ + public function getPassword() { + return $this->password; + } + + /** + * @inheritdoc + */ + public function setToken($token) { + $this->token = $token; + return $this; + } + + /** + * @inheritdoc + */ + public function getToken() { + return $this->token; + } + + /** + * Set the parent of this share + * + * @param int parent + * @return \OCP\Share\IShare + * @deprecated The new shares do not have parents. This is just here for legacy reasons. + */ + public function setParent($parent) { + $this->parent = $parent; + return $this; + } + + /** + * Get the parent of this share. + * + * @return int + * @deprecated The new shares do not have parents. This is just here for legacy reasons. + */ + public function getParent() { + return $this->parent; + } + + /** + * @inheritdoc + */ + public function setTarget($target) { + $this->target = $target; + return $this; + } + + /** + * @inheritdoc + */ + public function getTarget() { + return $this->target; + } + + /** + * @inheritdoc + */ + public function setShareTime(\DateTime $shareTime) { + $this->shareTime = $shareTime; + return $this; + } + + /** + * @inheritdoc + */ + public function getShareTime() { + return $this->shareTime; + } + + /** + * @inheritdoc + */ + public function setMailSend($mailSend) { + $this->mailSend = $mailSend; + return $this; + } + + /** + * @inheritdoc + */ + public function getMailSend() { + return $this->mailSend; + } +} |