Signed-off-by: Bjoern Schiessle <bjoern@schiessle.org>pull/657/head
parent
0a6f02801f
commit
a17c6a485d
@ -0,0 +1,14 @@ |
||||
<?xml version="1.0"?> |
||||
<info> |
||||
<id>sharebymail</id> |
||||
<name>Share by mail</name> |
||||
<description>Share provider which allows you to share files by mail</description> |
||||
<licence>AGPL</licence> |
||||
<author>Bjoern Schiessle</author> |
||||
<version>1.0.0</version> |
||||
<namespace>ShareByMail</namespace> |
||||
<category>other</category> |
||||
<dependencies> |
||||
<owncloud min-version="9.2" max-version="9.2" /> |
||||
</dependencies> |
||||
</info> |
||||
@ -0,0 +1,594 @@ |
||||
<?php |
||||
/** |
||||
* @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* 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 |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OCA\ShareByMail; |
||||
|
||||
use OCP\Files\IRootFolder; |
||||
use OCP\Files\Node; |
||||
use OCP\IDBConnection; |
||||
use OCP\IL10N; |
||||
use OCP\ILogger; |
||||
use OCP\IUserManager; |
||||
use OCP\Security\ISecureRandom; |
||||
use OC\Share20\Share; |
||||
use OCP\Share\IShare; |
||||
use OCP\Share\IShareProvider; |
||||
|
||||
/** |
||||
* Class ShareByMail |
||||
* |
||||
* @package OCA\ShareByMail |
||||
*/ |
||||
class ShareByMailProvider implements IShareProvider { |
||||
|
||||
/** @var IDBConnection */ |
||||
private $dbConnection; |
||||
|
||||
/** @var ILogger */ |
||||
private $logger; |
||||
|
||||
/** @var ISecureRandom */ |
||||
private $secureRandom; |
||||
|
||||
/** @var IUserManager */ |
||||
private $userManager; |
||||
|
||||
/** @var IRootFolder */ |
||||
private $rootFolder; |
||||
|
||||
/** @var IL10N */ |
||||
private $l; |
||||
|
||||
/** |
||||
* Return the identifier of this provider. |
||||
* |
||||
* @return string Containing only [a-zA-Z0-9] |
||||
*/ |
||||
public function identifier() { |
||||
return 'ocShareByMail'; |
||||
} |
||||
|
||||
/** |
||||
* DefaultShareProvider constructor. |
||||
* |
||||
* @param IDBConnection $connection |
||||
* @param ISecureRandom $secureRandom |
||||
* @param IUserManager $userManager |
||||
* @param IRootFolder $rootFolder |
||||
* @param IL10N $l |
||||
* @param ILogger $logger |
||||
*/ |
||||
public function __construct( |
||||
IDBConnection $connection, |
||||
ISecureRandom $secureRandom, |
||||
IUserManager $userManager, |
||||
IRootFolder $rootFolder, |
||||
IL10N $l, |
||||
ILogger $logger |
||||
) { |
||||
$this->dbConnection = $connection; |
||||
$this->secureRandom = $secureRandom; |
||||
$this->userManager = $userManager; |
||||
$this->rootFolder = $rootFolder; |
||||
$this->l = $l; |
||||
$this->logger = $logger; |
||||
} |
||||
|
||||
/** |
||||
* Share a path |
||||
* |
||||
* @param IShare $share |
||||
* @return IShare The share object |
||||
* @throws ShareNotFound |
||||
* @throws \Exception |
||||
*/ |
||||
public function create(IShare $share) { |
||||
|
||||
$shareWith = $share->getSharedWith(); |
||||
/* |
||||
* Check if file is not already shared with the remote user |
||||
*/ |
||||
$alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_EMAIL, $share->getNode(), 1, 0); |
||||
if (!empty($alreadyShared)) { |
||||
$message = 'Sharing %s failed, this item is already shared with %s'; |
||||
$message_t = $this->l->t('Sharing %s failed, this item is already shared with %s', array($share->getNode()->getName(), $shareWith)); |
||||
$this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']); |
||||
throw new \Exception($message_t); |
||||
} |
||||
|
||||
$shareId = $this->createMailShare($share); |
||||
|
||||
$data = $this->getRawShare($shareId); |
||||
return $this->createShareObject($data); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* @param IShare $share |
||||
* @return int |
||||
* @throws \Exception |
||||
*/ |
||||
private function createMailShare(IShare $share) { |
||||
$token = $this->generateToken(); |
||||
$shareId = $this->addShareToDB( |
||||
$share->getNodeId(), |
||||
$share->getNodeType(), |
||||
$share->getSharedWith(), |
||||
$share->getSharedBy(), |
||||
$share->getShareOwner(), |
||||
$share->getPermissions(), |
||||
$token |
||||
); |
||||
|
||||
try { |
||||
// TODO: Send email with public link |
||||
} catch (\Exception $e) { |
||||
$this->logger->error('Failed to notify remote server of federated share, removing share (' . $e->getMessage() . ')'); |
||||
$this->removeShareFromTable($shareId); |
||||
throw $e; |
||||
} |
||||
|
||||
return $shareId; |
||||
|
||||
} |
||||
|
||||
/** |
||||
* generate share token |
||||
* |
||||
* @return string |
||||
*/ |
||||
public function generateToken() { |
||||
$token = $this->secureRandom->generate( |
||||
15, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS); |
||||
return $token; |
||||
} |
||||
|
||||
/** |
||||
* Get all children of this share |
||||
* |
||||
* @param IShare $parent |
||||
* @return IShare[] |
||||
*/ |
||||
public function getChildren(IShare $parent) { |
||||
$children = []; |
||||
|
||||
$qb = $this->dbConnection->getQueryBuilder(); |
||||
$qb->select('*') |
||||
->from('share') |
||||
->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId()))) |
||||
->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
||||
->orderBy('id'); |
||||
|
||||
$cursor = $qb->execute(); |
||||
while($data = $cursor->fetch()) { |
||||
$children[] = $this->createShareObject($data); |
||||
} |
||||
$cursor->closeCursor(); |
||||
|
||||
return $children; |
||||
} |
||||
|
||||
/** |
||||
* add share to the database and return the ID |
||||
* |
||||
* @param int $itemSource |
||||
* @param string $itemType |
||||
* @param string $shareWith |
||||
* @param string $sharedBy |
||||
* @param string $uidOwner |
||||
* @param int $permissions |
||||
* @param string $token |
||||
* @return int |
||||
*/ |
||||
private function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token) { |
||||
$qb = $this->dbConnection->getQueryBuilder(); |
||||
$qb->insert('share') |
||||
->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)) |
||||
->setValue('item_type', $qb->createNamedParameter($itemType)) |
||||
->setValue('item_source', $qb->createNamedParameter($itemSource)) |
||||
->setValue('file_source', $qb->createNamedParameter($itemSource)) |
||||
->setValue('share_with', $qb->createNamedParameter($shareWith)) |
||||
->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) |
||||
->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) |
||||
->setValue('permissions', $qb->createNamedParameter($permissions)) |
||||
->setValue('token', $qb->createNamedParameter($token)) |
||||
->setValue('stime', $qb->createNamedParameter(time())); |
||||
|
||||
/* |
||||
* Added to fix https://github.com/owncloud/core/issues/22215 |
||||
* Can be removed once we get rid of ajax/share.php |
||||
*/ |
||||
$qb->setValue('file_target', $qb->createNamedParameter('')); |
||||
|
||||
$qb->execute(); |
||||
$id = $qb->getLastInsertId(); |
||||
|
||||
return (int)$id; |
||||
} |
||||
|
||||
/** |
||||
* Update a share |
||||
* |
||||
* @param IShare $share |
||||
* @return IShare The share object |
||||
*/ |
||||
public function update(IShare $share) { |
||||
/* |
||||
* We allow updating the permissions of mail shares |
||||
*/ |
||||
$qb = $this->dbConnection->getQueryBuilder(); |
||||
$qb->update('share') |
||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
||||
->set('permissions', $qb->createNamedParameter($share->getPermissions())) |
||||
->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) |
||||
->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) |
||||
->execute(); |
||||
|
||||
return $share; |
||||
} |
||||
|
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function move(IShare $share, $recipient) { |
||||
/** |
||||
* nothing to do here, mail shares are only outgoing shares |
||||
*/ |
||||
return $share; |
||||
} |
||||
|
||||
/** |
||||
* Delete a share (owner unShares the file) |
||||
* |
||||
* @param IShare $share |
||||
*/ |
||||
public function delete(IShare $share) { |
||||
$this->removeShareFromTable($share->getId()); |
||||
} |
||||
|
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function deleteFromSelf(IShare $share, $recipient) { |
||||
// nothing to do here, mail shares are only outgoing shares |
||||
return; |
||||
} |
||||
|
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) { |
||||
$qb = $this->dbConnection->getQueryBuilder(); |
||||
$qb->select('*') |
||||
->from('share'); |
||||
|
||||
$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); |
||||
|
||||
/** |
||||
* 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->createShareObject($data); |
||||
} |
||||
$cursor->closeCursor(); |
||||
|
||||
return $shares; |
||||
} |
||||
|
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function getShareById($id, $recipientId = null) { |
||||
$qb = $this->dbConnection->getQueryBuilder(); |
||||
|
||||
$qb->select('*') |
||||
->from('share') |
||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) |
||||
->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); |
||||
|
||||
$cursor = $qb->execute(); |
||||
$data = $cursor->fetch(); |
||||
$cursor->closeCursor(); |
||||
|
||||
if ($data === false) { |
||||
throw new ShareNotFound(); |
||||
} |
||||
|
||||
try { |
||||
$share = $this->createShareObject($data); |
||||
} catch (InvalidShare $e) { |
||||
throw new ShareNotFound(); |
||||
} |
||||
|
||||
return $share; |
||||
} |
||||
|
||||
/** |
||||
* Get shares for a given path |
||||
* |
||||
* @param \OCP\Files\Node $path |
||||
* @return IShare[] |
||||
*/ |
||||
public function getSharesByPath(Node $path) { |
||||
$qb = $this->dbConnection->getQueryBuilder(); |
||||
|
||||
$cursor = $qb->select('*') |
||||
->from('share') |
||||
->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) |
||||
->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
||||
->execute(); |
||||
|
||||
$shares = []; |
||||
while($data = $cursor->fetch()) { |
||||
$shares[] = $this->createShareObject($data); |
||||
} |
||||
$cursor->closeCursor(); |
||||
|
||||
return $shares; |
||||
} |
||||
|
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function getSharedWith($userId, $shareType, $node, $limit, $offset) { |
||||
/** @var IShare[] $shares */ |
||||
$shares = []; |
||||
|
||||
//Get shares directly with this user |
||||
$qb = $this->dbConnection->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_EMAIL))); |
||||
$qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))); |
||||
|
||||
// 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->createShareObject($data); |
||||
} |
||||
$cursor->closeCursor(); |
||||
|
||||
|
||||
return $shares; |
||||
} |
||||
|
||||
/** |
||||
* Get a share by token |
||||
* |
||||
* @param string $token |
||||
* @return IShare |
||||
* @throws ShareNotFound |
||||
*/ |
||||
public function getShareByToken($token) { |
||||
$qb = $this->dbConnection->getQueryBuilder(); |
||||
|
||||
$cursor = $qb->select('*') |
||||
->from('share') |
||||
->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
||||
->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) |
||||
->execute(); |
||||
|
||||
$data = $cursor->fetch(); |
||||
|
||||
if ($data === false) { |
||||
throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
||||
} |
||||
|
||||
try { |
||||
$share = $this->createShareObject($data); |
||||
} catch (InvalidShare $e) { |
||||
throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
||||
} |
||||
|
||||
return $share; |
||||
} |
||||
|
||||
/** |
||||
* remove share from table |
||||
* |
||||
* @param string $shareId |
||||
*/ |
||||
private function removeShareFromTable($shareId) { |
||||
$qb = $this->dbConnection->getQueryBuilder(); |
||||
$qb->delete('share') |
||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId))); |
||||
$qb->execute(); |
||||
} |
||||
|
||||
/** |
||||
* Create a share object from an database row |
||||
* |
||||
* @param array $data |
||||
* @return IShare |
||||
* @throws InvalidShare |
||||
* @throws ShareNotFound |
||||
*/ |
||||
private function createShareObject($data) { |
||||
|
||||
$share = new Share($this->rootFolder, $this->userManager); |
||||
$share->setId((int)$data['id']) |
||||
->setShareType((int)$data['share_type']) |
||||
->setPermissions((int)$data['permissions']) |
||||
->setTarget($data['file_target']) |
||||
->setMailSend((bool)$data['mail_send']) |
||||
->setToken($data['token']); |
||||
|
||||
$shareTime = new \DateTime(); |
||||
$shareTime->setTimestamp((int)$data['stime']); |
||||
$share->setShareTime($shareTime); |
||||
$share->setSharedWith($data['share_with']); |
||||
|
||||
if ($data['uid_initiator'] !== null) { |
||||
$share->setShareOwner($data['uid_owner']); |
||||
$share->setSharedBy($data['uid_initiator']); |
||||
} else { |
||||
//OLD SHARE |
||||
$share->setSharedBy($data['uid_owner']); |
||||
$path = $this->getNode($share->getSharedBy(), (int)$data['file_source']); |
||||
|
||||
$owner = $path->getOwner(); |
||||
$share->setShareOwner($owner->getUID()); |
||||
} |
||||
|
||||
$share->setNodeId((int)$data['file_source']); |
||||
$share->setNodeType($data['item_type']); |
||||
|
||||
$share->setProviderId($this->identifier()); |
||||
|
||||
return $share; |
||||
} |
||||
|
||||
/** |
||||
* Get the node with file $id for $user |
||||
* |
||||
* @param string $userId |
||||
* @param int $id |
||||
* @return \OCP\Files\File|\OCP\Files\Folder |
||||
* @throws InvalidShare |
||||
*/ |
||||
private function getNode($userId, $id) { |
||||
try { |
||||
$userFolder = $this->rootFolder->getUserFolder($userId); |
||||
} catch (NotFoundException $e) { |
||||
throw new InvalidShare(); |
||||
} |
||||
|
||||
$nodes = $userFolder->getById($id); |
||||
|
||||
if (empty($nodes)) { |
||||
throw new InvalidShare(); |
||||
} |
||||
|
||||
return $nodes[0]; |
||||
} |
||||
|
||||
/** |
||||
* A user is deleted from the system |
||||
* So clean up the relevant shares. |
||||
* |
||||
* @param string $uid |
||||
* @param int $shareType |
||||
*/ |
||||
public function userDeleted($uid, $shareType) { |
||||
$qb = $this->dbConnection->getQueryBuilder(); |
||||
|
||||
$qb->delete('share') |
||||
->where($qb->expr()->eq('share_type', $qb->createNamedParameter(Share::SHARE_TYPE_REMOTE))) |
||||
->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))) |
||||
->execute(); |
||||
} |
||||
|
||||
/** |
||||
* This provider does not support group shares |
||||
* |
||||
* @param string $gid |
||||
*/ |
||||
public function groupDeleted($gid) { |
||||
return; |
||||
} |
||||
|
||||
/** |
||||
* This provider does not support group shares |
||||
* |
||||
* @param string $uid |
||||
* @param string $gid |
||||
*/ |
||||
public function userDeletedFromGroup($uid, $gid) { |
||||
return; |
||||
} |
||||
|
||||
/** |
||||
* get database row of a give share |
||||
* |
||||
* @param $id |
||||
* @return array |
||||
* @throws ShareNotFound |
||||
*/ |
||||
private function getRawShare($id) { |
||||
|
||||
// Now fetch the inserted share and create a complete share object |
||||
$qb = $this->dbConnection->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; |
||||
} |
||||
|
||||
return $data; |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue