2015-10-30 15:09:07 +03:00
< ? php
/**
2016-07-21 18:07:57 +03:00
* @ copyright Copyright ( c ) 2016 , ownCloud , Inc .
*
2016-05-26 20:56:05 +03:00
* @ author Arthur Schiwon < blizzz @ arthur - schiwon . de >
2016-07-21 18:07:57 +03:00
* @ author Bjoern Schiessle < bjoern @ schiessle . org >
2016-05-26 20:56:05 +03:00
* @ author Björn Schießle < bjoern @ schiessle . org >
2019-12-03 21:57:53 +03:00
* @ author Christoph Wurst < christoph @ winzerhof - wurst . at >
2017-11-06 17:56:42 +03:00
* @ author Daniel Calviño Sánchez < danxuliu @ gmail . com >
* @ author Jan - Christoph Borchardt < hey @ jancborchardt . net >
2016-07-21 18:07:57 +03:00
* @ author Joas Schilling < coding @ schilljs . com >
2019-12-03 21:57:53 +03:00
* @ author John Molakvoæ ( skjnldsv ) < skjnldsv @ protonmail . com >
2017-11-06 17:56:42 +03:00
* @ author Julius Härtl < jus @ bitgrid . net >
* @ author Lukas Reschke < lukas @ statuscode . ch >
* @ author Maxence Lange < maxence @ artificial - owl . com >
* @ author Maxence Lange < maxence @ nextcloud . com >
* @ author Morris Jobke < hey @ morrisjobke . de >
* @ author Pauli Järvinen < pauli . jarvinen @ gmail . com >
* @ author Robin Appelman < robin @ icewind . nl >
2016-07-21 18:07:57 +03:00
* @ author Roeland Jago Douma < roeland @ famdouma . nl >
2019-12-03 21:57:53 +03:00
* @ author Thibault Coupin < thibault . coupin @ gmail . com >
2016-07-21 18:07:57 +03:00
* @ author Vincent Petry < pvince81 @ owncloud . com >
2015-10-30 15:09:07 +03:00
*
* @ 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 ,
2019-12-03 21:57:53 +03:00
* along with this program . If not , see < http :// www . gnu . org / licenses />
2015-10-30 15:09:07 +03:00
*
*/
2016-01-27 14:31:10 +03:00
2015-10-30 15:09:07 +03:00
namespace OC\Share20 ;
2016-05-03 12:58:33 +03:00
use OC\Cache\CappedMemoryCache ;
2016-03-30 14:33:16 +03:00
use OC\Files\Mount\MoveableMount ;
2016-06-24 18:53:37 +03:00
use OC\HintException ;
2016-10-24 18:03:36 +03:00
use OC\Share20\Exception\ProviderException ;
2020-08-13 11:06:26 +03:00
use OCA\Files_Sharing\ISharedStorage ;
2019-12-12 23:38:52 +03:00
use OCP\EventDispatcher\IEventDispatcher ;
2016-05-03 12:58:33 +03:00
use OCP\Files\File ;
use OCP\Files\Folder ;
2016-02-04 14:51:23 +03:00
use OCP\Files\IRootFolder ;
2016-05-03 12:58:33 +03:00
use OCP\Files\Mount\IMountManager ;
2016-06-18 23:04:56 +03:00
use OCP\Files\Node ;
2015-12-15 11:54:12 +03:00
use OCP\IConfig ;
2016-05-03 12:58:33 +03:00
use OCP\IGroupManager ;
2016-01-05 14:50:00 +03:00
use OCP\IL10N ;
2015-10-30 15:09:07 +03:00
use OCP\ILogger ;
2017-07-27 00:53:07 +03:00
use OCP\IURLGenerator ;
2017-08-01 18:50:57 +03:00
use OCP\IUser ;
2016-05-03 12:58:33 +03:00
use OCP\IUserManager ;
2017-09-08 11:34:19 +03:00
use OCP\L10N\IFactory ;
2017-07-27 00:53:07 +03:00
use OCP\Mail\IMailer ;
2019-11-19 21:18:00 +03:00
use OCP\Security\Events\ValidatePasswordPolicyEvent ;
2015-12-15 11:54:12 +03:00
use OCP\Security\IHasher ;
2016-05-03 12:58:33 +03:00
use OCP\Security\ISecureRandom ;
2019-11-22 22:52:10 +03:00
use OCP\Share ;
2016-02-02 16:18:59 +03:00
use OCP\Share\Exceptions\GenericShareException ;
2016-05-03 12:58:33 +03:00
use OCP\Share\Exceptions\ShareNotFound ;
use OCP\Share\IManager ;
use OCP\Share\IProviderFactory ;
2018-06-19 10:20:35 +03:00
use OCP\Share\IShare ;
2019-11-22 22:52:10 +03:00
use OCP\Share\IShareProvider ;
2019-05-28 20:46:36 +03:00
use Symfony\Component\EventDispatcher\EventDispatcherInterface ;
2016-06-24 18:53:37 +03:00
use Symfony\Component\EventDispatcher\GenericEvent ;
2015-10-30 15:09:07 +03:00
/**
* This class is the communication hub for all sharing related operations .
*/
2016-01-27 14:31:10 +03:00
class Manager implements IManager {
2015-10-30 15:09:07 +03:00
2016-01-11 12:30:03 +03:00
/** @var IProviderFactory */
private $factory ;
2015-10-30 15:09:07 +03:00
/** @var ILogger */
private $logger ;
2015-12-15 11:54:12 +03:00
/** @var IConfig */
private $config ;
/** @var ISecureRandom */
private $secureRandom ;
/** @var IHasher */
private $hasher ;
/** @var IMountManager */
private $mountManager ;
/** @var IGroupManager */
private $groupManager ;
2016-01-05 14:50:00 +03:00
/** @var IL10N */
private $l ;
2017-09-08 11:34:19 +03:00
/** @var IFactory */
private $l10nFactory ;
2016-02-02 23:02:09 +03:00
/** @var IUserManager */
private $userManager ;
2016-02-04 14:51:23 +03:00
/** @var IRootFolder */
private $rootFolder ;
2016-05-03 12:58:33 +03:00
/** @var CappedMemoryCache */
private $sharingDisabledForUsersCache ;
2019-05-28 20:46:36 +03:00
/** @var EventDispatcherInterface */
2019-12-12 23:38:52 +03:00
private $legacyDispatcher ;
2017-03-14 21:18:20 +03:00
/** @var LegacyHooks */
private $legacyHooks ;
2017-07-27 00:53:07 +03:00
/** @var IMailer */
private $mailer ;
/** @var IURLGenerator */
private $urlGenerator ;
/** @var \OC_Defaults */
private $defaults ;
2019-12-12 23:38:52 +03:00
/** @var IEventDispatcher */
private $dispatcher ;
2016-05-03 12:58:33 +03:00
2016-02-02 23:02:09 +03:00
2015-11-24 11:58:37 +03:00
/**
* Manager constructor .
*
* @ param ILogger $logger
2015-12-15 11:54:12 +03:00
* @ param IConfig $config
* @ param ISecureRandom $secureRandom
* @ param IHasher $hasher
* @ param IMountManager $mountManager
* @ param IGroupManager $groupManager
2016-01-05 14:50:00 +03:00
* @ param IL10N $l
2017-09-08 11:34:19 +03:00
* @ param IFactory $l10nFactory
2016-01-11 12:30:03 +03:00
* @ param IProviderFactory $factory
2016-02-02 23:02:09 +03:00
* @ param IUserManager $userManager
2016-02-04 15:25:24 +03:00
* @ param IRootFolder $rootFolder
2019-05-28 20:46:36 +03:00
* @ param EventDispatcherInterface $eventDispatcher
2017-07-27 00:53:07 +03:00
* @ param IMailer $mailer
* @ param IURLGenerator $urlGenerator
* @ param \OC_Defaults $defaults
2015-11-24 11:58:37 +03:00
*/
2015-11-23 16:06:25 +03:00
public function __construct (
ILogger $logger ,
2015-12-15 11:54:12 +03:00
IConfig $config ,
ISecureRandom $secureRandom ,
IHasher $hasher ,
IMountManager $mountManager ,
2016-01-05 14:50:00 +03:00
IGroupManager $groupManager ,
2016-01-11 12:30:03 +03:00
IL10N $l ,
2017-09-08 11:34:19 +03:00
IFactory $l10nFactory ,
2016-02-02 23:02:09 +03:00
IProviderFactory $factory ,
2016-02-04 14:51:23 +03:00
IUserManager $userManager ,
2016-06-24 18:53:37 +03:00
IRootFolder $rootFolder ,
2019-12-12 23:38:52 +03:00
EventDispatcherInterface $legacyDispatcher ,
2017-07-27 00:53:07 +03:00
IMailer $mailer ,
IURLGenerator $urlGenerator ,
2019-12-12 23:38:52 +03:00
\OC_Defaults $defaults ,
IEventDispatcher $dispatcher
2015-11-23 16:06:25 +03:00
) {
2015-10-30 15:09:07 +03:00
$this -> logger = $logger ;
2015-12-15 11:54:12 +03:00
$this -> config = $config ;
$this -> secureRandom = $secureRandom ;
$this -> hasher = $hasher ;
$this -> mountManager = $mountManager ;
$this -> groupManager = $groupManager ;
2016-01-05 14:50:00 +03:00
$this -> l = $l ;
2017-09-08 11:34:19 +03:00
$this -> l10nFactory = $l10nFactory ;
2016-01-11 12:30:03 +03:00
$this -> factory = $factory ;
2016-02-02 23:02:09 +03:00
$this -> userManager = $userManager ;
2016-02-04 14:51:23 +03:00
$this -> rootFolder = $rootFolder ;
2019-12-12 23:38:52 +03:00
$this -> legacyDispatcher = $legacyDispatcher ;
2016-05-03 12:58:33 +03:00
$this -> sharingDisabledForUsersCache = new CappedMemoryCache ();
2019-12-12 23:38:52 +03:00
$this -> legacyHooks = new LegacyHooks ( $this -> legacyDispatcher );
2017-07-27 00:53:07 +03:00
$this -> mailer = $mailer ;
$this -> urlGenerator = $urlGenerator ;
$this -> defaults = $defaults ;
2019-12-12 23:38:52 +03:00
$this -> dispatcher = $dispatcher ;
2016-01-08 16:31:28 +03:00
}
2015-10-30 15:09:07 +03:00
2016-01-13 15:02:23 +03:00
/**
* Convert from a full share id to a tuple ( providerId , shareId )
*
* @ param string $id
* @ return string []
*/
private function splitFullId ( $id ) {
return explode ( ':' , $id , 2 );
}
2015-12-15 11:54:12 +03:00
/**
* 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 ;
}
2016-06-27 11:46:39 +03:00
// Let others verify the password
2016-06-24 18:53:37 +03:00
try {
2019-12-12 23:38:52 +03:00
$this -> legacyDispatcher -> dispatch ( new ValidatePasswordPolicyEvent ( $password ));
2016-06-24 18:53:37 +03:00
} catch ( HintException $e ) {
throw new \Exception ( $e -> getHint ());
}
2015-12-15 11:54:12 +03:00
}
/**
* Check for generic requirements before creating a share
*
2016-01-27 14:13:53 +03:00
* @ param \OCP\Share\IShare $share
2016-02-02 16:18:59 +03:00
* @ throws \InvalidArgumentException
* @ throws GenericShareException
2017-07-18 21:38:40 +03:00
*
* @ suppress PhanUndeclaredClassMethod
2015-12-15 11:54:12 +03:00
*/
2016-01-27 14:13:53 +03:00
protected function generalCreateChecks ( \OCP\Share\IShare $share ) {
2015-12-15 11:54:12 +03:00
if ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_USER ) {
// We expect a valid user as sharedWith for user shares
2016-02-02 23:02:09 +03:00
if ( ! $this -> userManager -> userExists ( $share -> getSharedWith ())) {
throw new \InvalidArgumentException ( 'SharedWith is not a valid user' );
2015-12-15 11:54:12 +03:00
}
2020-04-10 11:35:09 +03:00
} elseif ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_GROUP ) {
2015-12-15 11:54:12 +03:00
// We expect a valid group as sharedWith for group shares
2016-02-02 23:02:09 +03:00
if ( ! $this -> groupManager -> groupExists ( $share -> getSharedWith ())) {
throw new \InvalidArgumentException ( 'SharedWith is not a valid group' );
2015-12-15 11:54:12 +03:00
}
2020-04-10 11:35:09 +03:00
} elseif ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_LINK ) {
2015-12-15 11:54:12 +03:00
if ( $share -> getSharedWith () !== null ) {
throw new \InvalidArgumentException ( 'SharedWith should be empty' );
}
2020-04-10 11:35:09 +03:00
} elseif ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_REMOTE ) {
2016-02-04 12:00:55 +03:00
if ( $share -> getSharedWith () === null ) {
throw new \InvalidArgumentException ( 'SharedWith should not be empty' );
}
2020-04-10 15:19:56 +03:00
} elseif ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_REMOTE_GROUP ) {
2018-06-13 15:19:59 +03:00
if ( $share -> getSharedWith () === null ) {
throw new \InvalidArgumentException ( 'SharedWith should not be empty' );
}
2020-04-10 11:35:09 +03:00
} elseif ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_EMAIL ) {
2016-07-29 16:38:31 +03:00
if ( $share -> getSharedWith () === null ) {
throw new \InvalidArgumentException ( 'SharedWith should not be empty' );
}
2020-04-10 11:35:09 +03:00
} elseif ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_CIRCLE ) {
2017-07-11 14:21:24 +03:00
$circle = \OCA\Circles\Api\v1\Circles :: detailsCircle ( $share -> getSharedWith ());
2017-03-17 22:48:33 +03:00
if ( $circle === null ) {
throw new \InvalidArgumentException ( 'SharedWith is not a valid circle' );
}
2020-04-10 11:35:09 +03:00
} elseif ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_ROOM ) {
2015-12-15 11:54:12 +03:00
} else {
// We can't handle other types yet
2017-03-17 22:48:33 +03:00
throw new \InvalidArgumentException ( 'unknown share type' );
2015-12-15 11:54:12 +03:00
}
2016-01-22 16:52:20 +03:00
// Verify the initiator of the share is set
2015-12-15 11:54:12 +03:00
if ( $share -> getSharedBy () === null ) {
throw new \InvalidArgumentException ( 'SharedBy should be set' );
}
// Cannot share with yourself
2016-02-02 23:02:09 +03:00
if ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_USER &&
$share -> getSharedWith () === $share -> getSharedBy ()) {
2017-05-17 16:35:10 +03:00
throw new \InvalidArgumentException ( 'Can’ t share with yourself' );
2015-12-15 11:54:12 +03:00
}
// The path should be set
2016-01-27 22:51:26 +03:00
if ( $share -> getNode () === null ) {
2015-12-15 11:54:12 +03:00
throw new \InvalidArgumentException ( 'Path should be set' );
}
2016-01-22 16:52:20 +03:00
2015-12-15 11:54:12 +03:00
// And it should be a file or a folder
2016-01-27 22:51:26 +03:00
if ( ! ( $share -> getNode () instanceof \OCP\Files\File ) &&
! ( $share -> getNode () instanceof \OCP\Files\Folder )) {
2015-12-15 11:54:12 +03:00
throw new \InvalidArgumentException ( 'Path should be either a file or a folder' );
}
2016-02-25 22:22:35 +03:00
// And you can't share your rootfolder
2016-05-11 21:48:27 +03:00
if ( $this -> userManager -> userExists ( $share -> getSharedBy ())) {
2019-07-02 11:22:30 +03:00
$userFolder = $this -> rootFolder -> getUserFolder ( $share -> getSharedBy ());
2016-05-11 21:48:27 +03:00
} else {
2019-07-02 11:22:30 +03:00
$userFolder = $this -> rootFolder -> getUserFolder ( $share -> getShareOwner ());
2016-05-11 21:48:27 +03:00
}
2020-06-03 10:29:19 +03:00
if ( $userFolder -> getId () === $share -> getNode () -> getId ()) {
2017-05-17 16:35:10 +03:00
throw new \InvalidArgumentException ( 'You can’ t share your root folder' );
2016-02-25 22:22:35 +03:00
}
2015-12-15 11:54:12 +03:00
// Check if we actually have share permissions
2016-01-27 22:51:26 +03:00
if ( ! $share -> getNode () -> isShareable ()) {
2020-02-18 13:02:11 +03:00
$path = $userFolder -> getRelativePath ( $share -> getNode () -> getPath ());
$message_t = $this -> l -> t ( 'You are not allowed to share %s' , [ $path ]);
2016-02-02 16:18:59 +03:00
throw new GenericShareException ( $message_t , $message_t , 404 );
2015-12-15 11:54:12 +03:00
}
// Permissions should be set
if ( $share -> getPermissions () === null ) {
throw new \InvalidArgumentException ( 'A share requires permissions' );
}
2019-07-29 16:00:07 +03:00
$isFederatedShare = $share -> getNode () -> getStorage () -> instanceOfStorage ( '\OCA\Files_Sharing\External\Storage' );
$permissions = 0 ;
2020-06-19 17:25:49 +03:00
2020-03-13 19:17:43 +03:00
if ( ! $isFederatedShare && $share -> getNode () -> getOwner () && $share -> getNode () -> getOwner () -> getUID () !== $share -> getSharedBy ()) {
2020-08-13 11:06:26 +03:00
$userMounts = array_filter ( $userFolder -> getById ( $share -> getNode () -> getId ()), function ( $mount ) {
// We need to filter since there might be other mountpoints that contain the file
// e.g. if the user has access to the same external storage that the file is originating from
return $mount -> getStorage () -> instanceOfStorage ( ISharedStorage :: class );
});
$userMount = array_shift ( $userMounts );
if ( $userMount === null ) {
throw new GenericShareException ( 'Could not get proper share mount for ' . $share -> getNode () -> getId () . '. Failing since else the next calls are called with null' );
}
$mount = $userMount -> getMountPoint ();
2019-07-03 17:32:45 +03:00
// When it's a reshare use the parent share permissions as maximum
2019-07-02 11:22:30 +03:00
$userMountPointId = $mount -> getStorageRootId ();
$userMountPoints = $userFolder -> getById ( $userMountPointId );
$userMountPoint = array_shift ( $userMountPoints );
2020-06-19 17:25:49 +03:00
if ( $userMountPoint === null ) {
throw new GenericShareException ( 'Could not get proper user mount for ' . $userMountPointId . '. Failing since else the next calls are called with null' );
}
2019-07-02 11:22:30 +03:00
/* Check if this is an incoming share */
$incomingShares = $this -> getSharedWith ( $share -> getSharedBy (), Share :: SHARE_TYPE_USER , $userMountPoint , - 1 , 0 );
$incomingShares = array_merge ( $incomingShares , $this -> getSharedWith ( $share -> getSharedBy (), Share :: SHARE_TYPE_GROUP , $userMountPoint , - 1 , 0 ));
2019-10-29 14:26:53 +03:00
$incomingShares = array_merge ( $incomingShares , $this -> getSharedWith ( $share -> getSharedBy (), Share :: SHARE_TYPE_CIRCLE , $userMountPoint , - 1 , 0 ));
2019-07-02 11:22:30 +03:00
$incomingShares = array_merge ( $incomingShares , $this -> getSharedWith ( $share -> getSharedBy (), Share :: SHARE_TYPE_ROOM , $userMountPoint , - 1 , 0 ));
/** @var \OCP\Share\IShare[] $incomingShares */
if ( ! empty ( $incomingShares )) {
foreach ( $incomingShares as $incomingShare ) {
$permissions |= $incomingShare -> getPermissions ();
}
}
2019-07-03 17:32:45 +03:00
} else {
/*
* Quick fix for #23536
* Non moveable mount points do not have update and delete permissions
* while we 'most likely' do have that on the storage .
*/
$permissions = $share -> getNode () -> getPermissions ();
2020-08-13 11:06:26 +03:00
if ( ! ( $share -> getNode () -> getMountPoint () instanceof MoveableMount )) {
2019-07-03 17:32:45 +03:00
$permissions |= \OCP\Constants :: PERMISSION_DELETE | \OCP\Constants :: PERMISSION_UPDATE ;
}
2016-03-30 14:33:16 +03:00
}
2015-12-15 11:54:12 +03:00
// Check that we do not share with more permissions than we have
2016-03-30 14:33:16 +03:00
if ( $share -> getPermissions () & ~ $permissions ) {
2020-02-18 13:02:11 +03:00
$path = $userFolder -> getRelativePath ( $share -> getNode () -> getPath ());
$message_t = $this -> l -> t ( 'Can’ t increase permissions of %s' , [ $path ]);
2016-02-02 16:18:59 +03:00
throw new GenericShareException ( $message_t , $message_t , 404 );
2015-12-15 11:54:12 +03:00
}
2016-06-07 13:28:02 +03:00
2015-12-15 11:54:12 +03:00
// Check that read permissions are always set
2016-06-07 13:28:02 +03:00
// Link shares are allowed to have no read permissions to allow upload to hidden folders
2017-03-30 18:03:04 +03:00
$noReadPermissionRequired = $share -> getShareType () === \OCP\Share :: SHARE_TYPE_LINK
|| $share -> getShareType () === \OCP\Share :: SHARE_TYPE_EMAIL ;
2017-03-29 12:58:04 +03:00
if ( ! $noReadPermissionRequired &&
2016-06-07 13:28:02 +03:00
( $share -> getPermissions () & \OCP\Constants :: PERMISSION_READ ) === 0 ) {
2015-12-15 11:54:12 +03:00
throw new \InvalidArgumentException ( 'Shares need at least read permissions' );
}
2016-04-15 10:02:01 +03:00
if ( $share -> getNode () instanceof \OCP\Files\File ) {
if ( $share -> getPermissions () & \OCP\Constants :: PERMISSION_DELETE ) {
2017-05-17 16:35:10 +03:00
$message_t = $this -> l -> t ( 'Files can’ t be shared with delete permissions' );
2016-04-15 10:02:01 +03:00
throw new GenericShareException ( $message_t );
}
if ( $share -> getPermissions () & \OCP\Constants :: PERMISSION_CREATE ) {
2017-05-17 16:35:10 +03:00
$message_t = $this -> l -> t ( 'Files can’ t be shared with create permissions' );
2016-04-15 10:02:01 +03:00
throw new GenericShareException ( $message_t );
}
}
2015-12-15 11:54:12 +03:00
}
2019-10-28 21:22:05 +03:00
/**
* 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 validateExpirationDateInternal ( \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 -> shareApiInternalDefaultExpireDate ()) {
$expirationDate = new \DateTime ();
$expirationDate -> setTime ( 0 , 0 , 0 );
2020-04-21 15:10:33 +03:00
$days = ( int ) $this -> config -> getAppValue ( 'core' , 'internal_defaultExpDays' , $this -> shareApiLinkDefaultExpireDays ());
if ( $days > $this -> shareApiLinkDefaultExpireDays ()) {
$days = $this -> shareApiLinkDefaultExpireDays ();
}
$expirationDate -> add ( new \DateInterval ( 'P' . $days . 'D' ));
2019-10-28 21:22:05 +03:00
}
// If we enforce the expiration date check that is does not exceed
if ( $this -> shareApiInternalDefaultExpireDateEnforced ()) {
if ( $expirationDate === null ) {
throw new \InvalidArgumentException ( 'Expiration date is enforced' );
}
$date = new \DateTime ();
$date -> setTime ( 0 , 0 , 0 );
$date -> add ( new \DateInterval ( 'P' . $this -> shareApiInternalDefaultExpireDays () . 'D' ));
if ( $date < $expirationDate ) {
$message = $this -> l -> t ( 'Can’ t set expiration date more than %s days in the future' , [ $this -> shareApiInternalDefaultExpireDays ()]);
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 ;
}
2015-12-15 11:54:12 +03:00
/**
* Validate if the expiration date fits the system settings
*
2016-02-02 16:18:59 +03:00
* @ param \OCP\Share\IShare $share The share to validate the expiration date of
2016-02-25 16:21:46 +03:00
* @ return \OCP\Share\IShare The modified share object
2016-02-02 16:18:59 +03:00
* @ throws GenericShareException
* @ throws \InvalidArgumentException
* @ throws \Exception
2015-12-15 11:54:12 +03:00
*/
2016-01-30 00:42:59 +03:00
protected function validateExpirationDate ( \OCP\Share\IShare $share ) {
$expirationDate = $share -> getExpirationDate ();
2015-12-15 11:54:12 +03:00
2016-01-26 16:29:30 +03:00
if ( $expirationDate !== null ) {
2015-12-15 11:54:12 +03:00
//Make sure the expiration date is a date
2016-01-26 16:29:30 +03:00
$expirationDate -> setTime ( 0 , 0 , 0 );
2015-12-15 11:54:12 +03:00
$date = new \DateTime ();
$date -> setTime ( 0 , 0 , 0 );
2016-01-26 16:29:30 +03:00
if ( $date >= $expirationDate ) {
2016-01-05 14:50:00 +03:00
$message = $this -> l -> t ( 'Expiration date is in the past' );
2016-02-02 16:18:59 +03:00
throw new GenericShareException ( $message , $message , 404 );
2015-12-15 11:54:12 +03:00
}
}
2016-02-25 16:21:46 +03:00
// 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 );
2020-04-21 15:10:33 +03:00
$days = ( int ) $this -> config -> getAppValue ( 'core' , 'link_defaultExpDays' , $this -> shareApiLinkDefaultExpireDays ());
if ( $days > $this -> shareApiLinkDefaultExpireDays ()) {
$days = $this -> shareApiLinkDefaultExpireDays ();
}
$expirationDate -> add ( new \DateInterval ( 'P' . $days . 'D' ));
2016-02-25 16:21:46 +03:00
}
2015-12-15 11:54:12 +03:00
// If we enforce the expiration date check that is does not exceed
if ( $this -> shareApiLinkDefaultExpireDateEnforced ()) {
2016-01-26 16:29:30 +03:00
if ( $expirationDate === null ) {
2015-12-15 11:54:12 +03:00
throw new \InvalidArgumentException ( 'Expiration date is enforced' );
}
$date = new \DateTime ();
$date -> setTime ( 0 , 0 , 0 );
$date -> add ( new \DateInterval ( 'P' . $this -> shareApiLinkDefaultExpireDays () . 'D' ));
2016-01-26 16:29:30 +03:00
if ( $date < $expirationDate ) {
2017-05-17 16:35:10 +03:00
$message = $this -> l -> t ( 'Can’ t set expiration date more than %s days in the future' , [ $this -> shareApiLinkDefaultExpireDays ()]);
2016-02-02 16:18:59 +03:00
throw new GenericShareException ( $message , $message , 404 );
2015-12-15 11:54:12 +03:00
}
}
2016-01-30 00:42:59 +03:00
$accepted = true ;
$message = '' ;
\OCP\Util :: emitHook ( '\OC\Share' , 'verifyExpirationDate' , [
'expirationDate' => & $expirationDate ,
'accepted' => & $accepted ,
'message' => & $message ,
2016-02-25 17:39:02 +03:00
'passwordSet' => $share -> getPassword () !== null ,
2016-01-30 00:42:59 +03:00
]);
if ( ! $accepted ) {
throw new \Exception ( $message );
2015-12-15 11:54:12 +03:00
}
2016-01-30 00:42:59 +03:00
$share -> setExpirationDate ( $expirationDate );
2016-02-25 16:21:46 +03:00
return $share ;
2015-12-15 11:54:12 +03:00
}
/**
2016-01-08 16:31:28 +03:00
* Check for pre share requirements for user shares
2015-12-15 11:54:12 +03:00
*
2016-01-27 14:13:53 +03:00
* @ param \OCP\Share\IShare $share
2015-12-15 11:54:12 +03:00
* @ throws \Exception
*/
2016-01-27 14:13:53 +03:00
protected function userCreateChecks ( \OCP\Share\IShare $share ) {
2015-12-15 11:54:12 +03:00
// Check if we can share with group members only
if ( $this -> shareWithGroupMembersOnly ()) {
2016-02-02 23:02:09 +03:00
$sharedBy = $this -> userManager -> get ( $share -> getSharedBy ());
$sharedWith = $this -> userManager -> get ( $share -> getSharedWith ());
2015-12-15 11:54:12 +03:00
// Verify we can share with this user
$groups = array_intersect (
2016-02-02 23:02:09 +03:00
$this -> groupManager -> getUserGroupIds ( $sharedBy ),
$this -> groupManager -> getUserGroupIds ( $sharedWith )
2015-12-15 11:54:12 +03:00
);
if ( empty ( $groups )) {
2017-05-17 16:35:10 +03:00
throw new \Exception ( 'Sharing is only allowed with group members' );
2015-12-15 11:54:12 +03:00
}
}
/*
* TODO : Could be costly , fix
*
* Also this is not what we want in the future .. then we want to squash identical shares .
*/
2016-01-13 16:21:55 +03:00
$provider = $this -> factory -> getProviderForType ( \OCP\Share :: SHARE_TYPE_USER );
2016-01-27 22:51:26 +03:00
$existingShares = $provider -> getSharesByPath ( $share -> getNode ());
2020-04-10 15:19:56 +03:00
foreach ( $existingShares as $existingShare ) {
2016-01-22 16:52:20 +03:00
// Ignore if it is the same share
2016-02-08 23:08:03 +03:00
try {
if ( $existingShare -> getFullId () === $share -> getFullId ()) {
continue ;
}
} catch ( \UnexpectedValueException $e ) {
//Shares are not identical
2016-01-22 16:52:20 +03:00
}
2015-12-15 11:54:12 +03:00
// Identical share already existst
2019-03-20 14:50:48 +03:00
if ( $existingShare -> getSharedWith () === $share -> getSharedWith () && $existingShare -> getShareType () === $share -> getShareType ()) {
2017-05-17 16:35:10 +03:00
throw new \Exception ( 'Path is already shared with this user' );
2015-12-15 11:54:12 +03:00
}
// The share is already shared with this user via a group share
2016-02-02 23:02:09 +03:00
if ( $existingShare -> getShareType () === \OCP\Share :: SHARE_TYPE_GROUP ) {
$group = $this -> groupManager -> get ( $existingShare -> getSharedWith ());
2017-01-19 17:02:46 +03:00
if ( ! is_null ( $group )) {
$user = $this -> userManager -> get ( $share -> getSharedWith ());
2016-02-02 23:02:09 +03:00
2017-01-19 17:02:46 +03:00
if ( $group -> inGroup ( $user ) && $existingShare -> getShareOwner () !== $share -> getShareOwner ()) {
2017-05-17 16:35:10 +03:00
throw new \Exception ( 'Path is already shared with this user' );
2017-01-19 17:02:46 +03:00
}
2016-02-02 23:02:09 +03:00
}
2015-12-15 11:54:12 +03:00
}
}
}
/**
* Check for pre share requirements for group shares
*
2016-01-27 14:13:53 +03:00
* @ param \OCP\Share\IShare $share
2015-12-15 11:54:12 +03:00
* @ throws \Exception
*/
2016-01-27 14:13:53 +03:00
protected function groupCreateChecks ( \OCP\Share\IShare $share ) {
2016-03-18 18:36:27 +03:00
// Verify group shares are allowed
if ( ! $this -> allowGroupSharing ()) {
throw new \Exception ( 'Group sharing is now allowed' );
}
2015-12-15 11:54:12 +03:00
// Verify if the user can share with this group
if ( $this -> shareWithGroupMembersOnly ()) {
2016-02-02 23:02:09 +03:00
$sharedBy = $this -> userManager -> get ( $share -> getSharedBy ());
$sharedWith = $this -> groupManager -> get ( $share -> getSharedWith ());
2017-01-19 17:02:46 +03:00
if ( is_null ( $sharedWith ) || ! $sharedWith -> inGroup ( $sharedBy )) {
2017-05-17 16:35:10 +03:00
throw new \Exception ( 'Sharing is only allowed within your own groups' );
2015-12-15 11:54:12 +03:00
}
}
/*
* TODO : Could be costly , fix
*
* Also this is not what we want in the future .. then we want to squash identical shares .
*/
2016-01-13 16:21:55 +03:00
$provider = $this -> factory -> getProviderForType ( \OCP\Share :: SHARE_TYPE_GROUP );
2016-01-27 22:51:26 +03:00
$existingShares = $provider -> getSharesByPath ( $share -> getNode ());
2020-04-10 15:19:56 +03:00
foreach ( $existingShares as $existingShare ) {
2016-02-08 17:28:36 +03:00
try {
if ( $existingShare -> getFullId () === $share -> getFullId ()) {
continue ;
}
} catch ( \UnexpectedValueException $e ) {
//It is a new share so just continue
2016-01-22 16:52:20 +03:00
}
2019-03-20 14:51:26 +03:00
if ( $existingShare -> getSharedWith () === $share -> getSharedWith () && $existingShare -> getShareType () === $share -> getShareType ()) {
2017-05-17 16:35:10 +03:00
throw new \Exception ( 'Path is already shared with this group' );
2015-12-15 11:54:12 +03:00
}
}
}
/**
* Check for pre share requirements for link shares
*
2016-01-27 14:13:53 +03:00
* @ param \OCP\Share\IShare $share
2015-12-15 11:54:12 +03:00
* @ throws \Exception
*/
2016-01-27 14:13:53 +03:00
protected function linkCreateChecks ( \OCP\Share\IShare $share ) {
2015-12-15 11:54:12 +03:00
// Are link shares allowed?
if ( ! $this -> shareApiAllowLinks ()) {
2017-05-17 16:35:10 +03:00
throw new \Exception ( 'Link sharing is not allowed' );
2015-12-15 11:54:12 +03:00
}
// Check if public upload is allowed
if ( ! $this -> shareApiLinkAllowPublicUpload () &&
2016-06-23 16:43:21 +03:00
( $share -> getPermissions () & ( \OCP\Constants :: PERMISSION_CREATE | \OCP\Constants :: PERMISSION_UPDATE | \OCP\Constants :: PERMISSION_DELETE ))) {
2017-05-17 16:35:10 +03:00
throw new \InvalidArgumentException ( 'Public upload is not allowed' );
2015-12-15 11:54:12 +03:00
}
}
2016-02-11 13:55:09 +03:00
/**
* 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' )) {
2016-12-07 09:11:32 +03:00
/** @var \OCA\Files_Sharing\SharedStorage $storage */
2016-02-11 13:55:09 +03:00
$share -> setParent ( $storage -> getShareId ());
}
2018-01-27 01:46:40 +03:00
}
2016-02-11 13:55:09 +03:00
}
2015-12-15 11:54:12 +03:00
/**
* @ 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 ());
2020-04-10 15:19:56 +03:00
foreach ( $mounts as $mount ) {
2015-12-15 11:54:12 +03:00
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
*
2016-01-27 14:13:53 +03:00
* @ param \OCP\Share\IShare $share
2016-02-16 12:22:49 +03:00
* @ throws \Exception
2015-12-15 11:54:12 +03:00
*/
2016-01-27 14:13:53 +03:00
protected function canShare ( \OCP\Share\IShare $share ) {
2015-12-15 11:54:12 +03:00
if ( ! $this -> shareApiEnabled ()) {
2017-05-17 16:35:10 +03:00
throw new \Exception ( 'Sharing is disabled' );
2015-12-15 11:54:12 +03:00
}
2016-01-27 22:51:26 +03:00
if ( $this -> sharingDisabledForUser ( $share -> getSharedBy ())) {
2017-05-17 16:35:10 +03:00
throw new \Exception ( 'Sharing is disabled for you' );
2015-12-15 11:54:12 +03:00
}
}
2015-10-30 15:09:07 +03:00
/**
* Share a path
2015-11-24 11:58:37 +03:00
*
2016-01-27 14:13:53 +03:00
* @ param \OCP\Share\IShare $share
2015-10-30 15:09:07 +03:00
* @ return Share The share object
2015-12-15 11:54:12 +03:00
* @ throws \Exception
*
* TODO : handle link share permissions or check them
2015-10-30 15:09:07 +03:00
*/
2016-01-27 14:13:53 +03:00
public function createShare ( \OCP\Share\IShare $share ) {
2016-02-16 12:22:49 +03:00
$this -> canShare ( $share );
2015-12-15 11:54:12 +03:00
$this -> generalCreateChecks ( $share );
2016-04-15 10:02:01 +03:00
// 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 ();
2020-04-10 15:19:56 +03:00
while ( $parent -> getStorage () -> instanceOfStorage ( 'OCA\Files_Sharing\External\Storage' )) {
2016-04-15 10:02:01 +03:00
$parent = $parent -> getParent ();
}
$share -> setShareOwner ( $parent -> getOwner () -> getUID ());
} else {
2020-03-13 19:17:43 +03:00
if ( $share -> getNode () -> getOwner ()) {
$share -> setShareOwner ( $share -> getNode () -> getOwner () -> getUID ());
} else {
$share -> setShareOwner ( $share -> getSharedBy ());
}
2016-04-15 10:02:01 +03:00
}
2015-12-15 11:54:12 +03:00
//Verify share type
if ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_USER ) {
$this -> userCreateChecks ( $share );
2019-10-28 21:22:05 +03:00
//Verify the expiration date
$share = $this -> validateExpirationDateInternal ( $share );
2020-04-10 11:35:09 +03:00
} elseif ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_GROUP ) {
2015-12-15 11:54:12 +03:00
$this -> groupCreateChecks ( $share );
2019-10-28 21:22:05 +03:00
//Verify the expiration date
$share = $this -> validateExpirationDateInternal ( $share );
2020-04-10 11:35:09 +03:00
} elseif ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_LINK ) {
2015-12-15 11:54:12 +03:00
$this -> linkCreateChecks ( $share );
2016-02-11 13:55:09 +03:00
$this -> setLinkParent ( $share );
2015-12-15 11:54:12 +03:00
/*
* For now ignore a set token .
*/
$share -> setToken (
2016-01-05 14:50:00 +03:00
$this -> secureRandom -> generate (
2015-12-15 11:54:12 +03:00
\OC\Share\Constants :: TOKEN_LENGTH ,
2017-07-14 15:03:25 +03:00
\OCP\Security\ISecureRandom :: CHAR_HUMAN_READABLE
2015-12-15 11:54:12 +03:00
)
);
//Verify the expiration date
2019-10-28 21:22:05 +03:00
$share = $this -> validateExpirationDate ( $share );
2015-12-15 11:54:12 +03:00
//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 ()));
}
2020-04-10 11:35:09 +03:00
} elseif ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_EMAIL ) {
2016-07-29 16:38:31 +03:00
$share -> setToken (
$this -> secureRandom -> generate (
\OC\Share\Constants :: TOKEN_LENGTH ,
2017-07-14 15:03:25 +03:00
\OCP\Security\ISecureRandom :: CHAR_HUMAN_READABLE
2016-07-29 16:38:31 +03:00
)
);
2015-12-15 11:54:12 +03:00
}
2016-01-22 16:52:20 +03:00
// Cannot share with the owner
2016-02-02 23:02:09 +03:00
if ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_USER &&
$share -> getSharedWith () === $share -> getShareOwner ()) {
2017-05-17 16:35:10 +03:00
throw new \InvalidArgumentException ( 'Can’ t share with the share owner' );
2016-01-22 16:52:20 +03:00
}
2015-12-15 11:54:12 +03:00
// Generate the target
2016-01-27 22:51:26 +03:00
$target = $this -> config -> getSystemValue ( 'share_folder' , '/' ) . '/' . $share -> getNode () -> getName ();
2015-12-15 11:54:12 +03:00
$target = \OC\Files\Filesystem :: normalizePath ( $target );
$share -> setTarget ( $target );
2017-08-03 16:14:29 +03:00
// Pre share event
$event = new GenericEvent ( $share );
2019-12-12 23:38:52 +03:00
$this -> legacyDispatcher -> dispatch ( 'OCP\Share::preShare' , $event );
2017-08-03 16:14:29 +03:00
if ( $event -> isPropagationStopped () && $event -> hasArgument ( 'error' )) {
throw new \Exception ( $event -> getArgument ( 'error' ));
2015-12-15 11:54:12 +03:00
}
2016-09-20 19:00:20 +03:00
$oldShare = $share ;
2016-01-13 16:21:55 +03:00
$provider = $this -> factory -> getProviderForType ( $share -> getShareType ());
2016-01-08 16:31:28 +03:00
$share = $provider -> create ( $share );
2016-09-20 19:00:20 +03:00
//reuse the node we already have
$share -> setNode ( $oldShare -> getNode ());
2015-12-15 11:54:12 +03:00
2020-03-16 15:38:55 +03:00
// Reset the target if it is null for the new share
if ( $share -> getTarget () === '' ) {
$share -> setTarget ( $target );
}
2017-08-03 16:14:29 +03:00
// Post share event
$event = new GenericEvent ( $share );
2019-12-12 23:38:52 +03:00
$this -> legacyDispatcher -> dispatch ( 'OCP\Share::postShare' , $event );
$this -> dispatcher -> dispatchTyped ( new Share\Events\ShareCreatedEvent ( $share ));
2015-12-15 11:54:12 +03:00
2020-08-19 10:03:58 +03:00
if ( $this -> config -> getSystemValueBool ( 'sharing.enable_share_mail' , true )
&& $share -> getShareType () === \OCP\Share :: SHARE_TYPE_USER ) {
2017-09-11 01:28:24 +03:00
$mailSend = $share -> getMailSend ();
2020-04-10 15:19:56 +03:00
if ( $mailSend === true ) {
2017-09-11 01:28:24 +03:00
$user = $this -> userManager -> get ( $share -> getSharedWith ());
if ( $user !== null ) {
$emailAddress = $user -> getEMailAddress ();
if ( $emailAddress !== null && $emailAddress !== '' ) {
$userLang = $this -> config -> getUserValue ( $share -> getSharedWith (), 'core' , 'lang' , null );
$l = $this -> l10nFactory -> get ( 'lib' , $userLang );
$this -> sendMailNotification (
$l ,
$share -> getNode () -> getName (),
2020-01-06 18:48:21 +03:00
$this -> urlGenerator -> linkToRouteAbsolute ( 'files_sharing.Accept.accept' , [ 'shareId' => $share -> getFullId ()]),
2017-09-11 01:28:24 +03:00
$share -> getSharedBy (),
$emailAddress ,
$share -> getExpirationDate ()
);
2019-01-30 17:32:19 +03:00
$this -> logger -> debug ( 'Sent share notification to ' . $emailAddress . ' for share with ID ' . $share -> getId (), [ 'app' => 'share' ]);
2017-09-11 01:28:24 +03:00
} else {
2019-01-30 17:32:19 +03:00
$this -> logger -> debug ( 'Share notification not sent to ' . $share -> getSharedWith () . ' because email address is not set.' , [ 'app' => 'share' ]);
2017-09-11 01:28:24 +03:00
}
2017-07-27 00:53:07 +03:00
} else {
2019-01-30 17:32:19 +03:00
$this -> logger -> debug ( 'Share notification not sent to ' . $share -> getSharedWith () . ' because user could not be found.' , [ 'app' => 'share' ]);
2017-07-27 00:53:07 +03:00
}
} else {
2019-01-30 17:32:19 +03:00
$this -> logger -> debug ( 'Share notification not sent because mailsend is false.' , [ 'app' => 'share' ]);
2017-07-27 00:53:07 +03:00
}
}
2015-12-15 11:54:12 +03:00
return $share ;
2015-10-30 15:09:07 +03:00
}
2017-07-27 00:53:07 +03:00
/**
2019-01-30 17:32:19 +03:00
* Send mail notifications
*
* This method will catch and log mail transmission errors
*
2017-09-08 11:34:19 +03:00
* @ param IL10N $l Language of the recipient
2017-07-27 00:53:07 +03:00
* @ param string $filename file / folder name
* @ param string $link link to the file / folder
* @ param string $initiator user ID of share sender
* @ param string $shareWith email address of share receiver
2017-08-28 18:39:29 +03:00
* @ param \DateTime | null $expiration
2017-07-27 00:53:07 +03:00
*/
2017-09-08 11:34:19 +03:00
protected function sendMailNotification ( IL10N $l ,
$filename ,
2017-07-27 00:53:07 +03:00
$link ,
$initiator ,
2017-08-24 18:54:22 +03:00
$shareWith ,
2017-08-28 18:39:29 +03:00
\DateTime $expiration = null ) {
2017-07-27 00:53:07 +03:00
$initiatorUser = $this -> userManager -> get ( $initiator );
$initiatorDisplayName = ( $initiatorUser instanceof IUser ) ? $initiatorUser -> getDisplayName () : $initiator ;
$message = $this -> mailer -> createMessage ();
2017-09-04 16:07:19 +03:00
$emailTemplate = $this -> mailer -> createEMailTemplate ( 'files_sharing.RecipientNotification' , [
2017-08-24 18:54:22 +03:00
'filename' => $filename ,
'link' => $link ,
'initiator' => $initiatorDisplayName ,
'expiration' => $expiration ,
2017-08-29 17:02:30 +03:00
'shareWith' => $shareWith ,
2017-08-24 18:54:22 +03:00
]);
2017-07-27 00:53:07 +03:00
2020-03-26 11:30:18 +03:00
$emailTemplate -> setSubject ( $l -> t ( '%1$s shared »%2$s« with you' , [ $initiatorDisplayName , $filename ]));
2017-07-27 00:53:07 +03:00
$emailTemplate -> addHeader ();
2018-10-09 15:32:14 +03:00
$emailTemplate -> addHeading ( $l -> t ( '%1$s shared »%2$s« with you' , [ $initiatorDisplayName , $filename ]), false );
$text = $l -> t ( '%1$s shared »%2$s« with you.' , [ $initiatorDisplayName , $filename ]);
2017-07-27 00:53:07 +03:00
$emailTemplate -> addBodyText (
2018-02-15 14:18:51 +03:00
htmlspecialchars ( $text . ' ' . $l -> t ( 'Click the button below to open it.' )),
2017-07-27 00:53:07 +03:00
$text
);
$emailTemplate -> addBodyButton (
2017-09-08 11:34:19 +03:00
$l -> t ( 'Open »%s«' , [ $filename ]),
2017-07-27 00:53:07 +03:00
$link
);
$message -> setTo ([ $shareWith ]);
// The "From" contains the sharers name
$instanceName = $this -> defaults -> getName ();
2017-09-08 11:34:19 +03:00
$senderName = $l -> t (
2018-10-09 15:32:14 +03:00
'%1$s via %2$s' ,
2017-07-27 00:53:07 +03:00
[
$initiatorDisplayName ,
$instanceName
]
);
$message -> setFrom ([ \OCP\Util :: getDefaultEmailAddress ( $instanceName ) => $senderName ]);
// The "Reply-To" is set to the sharer if an mail address is configured
// also the default footer contains a "Do not reply" which needs to be adjusted.
$initiatorEmail = $initiatorUser -> getEMailAddress ();
2020-04-10 15:19:56 +03:00
if ( $initiatorEmail !== null ) {
2017-07-27 00:53:07 +03:00
$message -> setReplyTo ([ $initiatorEmail => $initiatorDisplayName ]);
2020-03-31 15:02:39 +03:00
$emailTemplate -> addFooter ( $instanceName . ( $this -> defaults -> getSlogan ( $l -> getLanguageCode ()) !== '' ? ' - ' . $this -> defaults -> getSlogan ( $l -> getLanguageCode ()) : '' ));
2017-07-27 00:53:07 +03:00
} else {
2020-03-31 15:02:39 +03:00
$emailTemplate -> addFooter ( '' , $l -> getLanguageCode ());
2017-07-27 00:53:07 +03:00
}
2017-09-15 12:01:21 +03:00
$message -> useTemplate ( $emailTemplate );
2018-11-23 18:53:32 +03:00
try {
$failedRecipients = $this -> mailer -> send ( $message );
2020-04-10 15:19:56 +03:00
if ( ! empty ( $failedRecipients )) {
2018-12-04 21:33:30 +03:00
$this -> logger -> error ( 'Share notification mail could not be sent to: ' . implode ( ', ' , $failedRecipients ));
2019-01-30 17:32:19 +03:00
return ;
2018-11-23 18:53:32 +03:00
}
} catch ( \Exception $e ) {
2018-12-04 21:33:30 +03:00
$this -> logger -> logException ( $e , [ 'message' => 'Share notification mail could not be sent' ]);
2018-11-23 18:53:32 +03:00
}
2017-07-27 00:53:07 +03:00
}
2015-10-30 15:09:07 +03:00
/**
* Update a share
*
2016-01-27 14:13:53 +03:00
* @ param \OCP\Share\IShare $share
* @ return \OCP\Share\IShare The share object
2016-02-02 23:02:09 +03:00
* @ throws \InvalidArgumentException
2015-10-30 15:09:07 +03:00
*/
2016-01-27 14:13:53 +03:00
public function updateShare ( \OCP\Share\IShare $share ) {
2016-01-25 15:09:25 +03:00
$expirationDateUpdated = false ;
2016-02-16 12:22:49 +03:00
$this -> canShare ( $share );
2016-01-22 16:52:20 +03:00
2016-02-08 23:08:03 +03:00
try {
$originalShare = $this -> getShareById ( $share -> getFullId ());
} catch ( \UnexpectedValueException $e ) {
throw new \InvalidArgumentException ( 'Share does not have a full id' );
}
2016-01-22 16:52:20 +03:00
// We can't change the share type!
if ( $share -> getShareType () !== $originalShare -> getShareType ()) {
2017-05-17 16:35:10 +03:00
throw new \InvalidArgumentException ( 'Can’ t change share type' );
2016-01-22 16:52:20 +03:00
}
// We can only change the recipient on user shares
if ( $share -> getSharedWith () !== $originalShare -> getSharedWith () &&
2020-04-09 10:22:29 +03:00
$share -> getShareType () !== \OCP\Share :: SHARE_TYPE_USER ) {
2016-01-26 16:29:30 +03:00
throw new \InvalidArgumentException ( 'Can only update recipient on user shares' );
2016-01-22 16:52:20 +03:00
}
// Cannot share with the owner
2016-02-02 23:02:09 +03:00
if ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_USER &&
$share -> getSharedWith () === $share -> getShareOwner ()) {
2017-05-17 16:35:10 +03:00
throw new \InvalidArgumentException ( 'Can’ t share with the share owner' );
2016-01-22 16:52:20 +03:00
}
$this -> generalCreateChecks ( $share );
if ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_USER ) {
$this -> userCreateChecks ( $share );
2019-10-28 21:22:05 +03:00
if ( $share -> getExpirationDate () != $originalShare -> getExpirationDate ()) {
//Verify the expiration date
$this -> validateExpirationDate ( $share );
$expirationDateUpdated = true ;
}
2020-04-10 11:35:09 +03:00
} elseif ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_GROUP ) {
2016-01-22 16:52:20 +03:00
$this -> groupCreateChecks ( $share );
2019-10-28 21:22:05 +03:00
if ( $share -> getExpirationDate () != $originalShare -> getExpirationDate ()) {
//Verify the expiration date
$this -> validateExpirationDate ( $share );
$expirationDateUpdated = true ;
}
2020-04-10 11:35:09 +03:00
} elseif ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_LINK ) {
2016-01-22 16:52:20 +03:00
$this -> linkCreateChecks ( $share );
2020-05-28 21:29:28 +03:00
$plainTextPassword = $share -> getPassword ();
2017-04-23 20:47:28 +03:00
$this -> updateSharePasswordIfNeeded ( $share , $originalShare );
2016-01-22 16:52:20 +03:00
2020-05-28 21:29:28 +03:00
if ( empty ( $plainTextPassword ) && $share -> getSendPasswordByTalk ()) {
throw new \InvalidArgumentException ( 'Can’ t enable sending the password by Talk with an empty password' );
}
2016-02-09 01:21:22 +03:00
if ( $share -> getExpirationDate () != $originalShare -> getExpirationDate ()) {
2016-01-25 15:09:25 +03:00
//Verify the expiration date
2016-01-30 00:42:59 +03:00
$this -> validateExpirationDate ( $share );
2016-01-25 15:09:25 +03:00
$expirationDateUpdated = true ;
}
2020-04-10 11:35:09 +03:00
} elseif ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_EMAIL ) {
2018-07-10 13:36:28 +03:00
// The new password is not set again if it is the same as the old
2020-05-28 21:40:33 +03:00
// one.
2017-04-23 20:47:28 +03:00
$plainTextPassword = $share -> getPassword ();
2020-05-28 21:40:33 +03:00
if ( ! empty ( $plainTextPassword ) && ! $this -> updateSharePasswordIfNeeded ( $share , $originalShare )) {
2017-04-23 20:47:28 +03:00
$plainTextPassword = null ;
2017-03-28 15:39:38 +03:00
}
2018-07-10 13:36:28 +03:00
if ( empty ( $plainTextPassword ) && ! $originalShare -> getSendPasswordByTalk () && $share -> getSendPasswordByTalk ()) {
// If the same password was already sent by mail the recipient
// would already have access to the share without having to call
// the sharer to verify her identity
throw new \InvalidArgumentException ( 'Can’ t enable sending the password by Talk without setting a new password' );
2020-05-28 21:40:33 +03:00
} elseif ( empty ( $plainTextPassword ) && $originalShare -> getSendPasswordByTalk () && ! $share -> getSendPasswordByTalk ()) {
throw new \InvalidArgumentException ( 'Can’ t disable sending the password by Talk without setting a new password' );
2018-07-10 13:36:28 +03:00
}
2017-03-28 15:39:38 +03:00
}
2016-01-27 22:51:26 +03:00
$this -> pathCreateChecks ( $share -> getNode ());
2016-01-22 16:52:20 +03:00
// Now update the share!
$provider = $this -> factory -> getProviderForType ( $share -> getShareType ());
2017-03-28 15:39:38 +03:00
if ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_EMAIL ) {
$share = $provider -> update ( $share , $plainTextPassword );
} else {
$share = $provider -> update ( $share );
}
2016-01-25 15:09:25 +03:00
if ( $expirationDateUpdated === true ) {
2018-01-26 01:16:13 +03:00
\OC_Hook :: emit ( Share :: class , 'post_set_expiration_date' , [
2016-01-27 22:51:26 +03:00
'itemType' => $share -> getNode () instanceof \OCP\Files\File ? 'file' : 'folder' ,
'itemSource' => $share -> getNode () -> getId (),
2016-01-25 15:09:25 +03:00
'date' => $share -> getExpirationDate (),
2016-02-02 23:02:09 +03:00
'uidOwner' => $share -> getSharedBy (),
2016-01-25 15:09:25 +03:00
]);
}
2016-02-09 12:38:12 +03:00
if ( $share -> getPassword () !== $originalShare -> getPassword ()) {
2018-01-26 01:16:13 +03:00
\OC_Hook :: emit ( Share :: class , 'post_update_password' , [
2016-02-09 12:38:12 +03:00
'itemType' => $share -> getNode () instanceof \OCP\Files\File ? 'file' : 'folder' ,
'itemSource' => $share -> getNode () -> getId (),
'uidOwner' => $share -> getSharedBy (),
'token' => $share -> getToken (),
'disabled' => is_null ( $share -> getPassword ()),
]);
}
2016-02-04 11:53:47 +03:00
if ( $share -> getPermissions () !== $originalShare -> getPermissions ()) {
2016-05-11 21:48:27 +03:00
if ( $this -> userManager -> userExists ( $share -> getShareOwner ())) {
$userFolder = $this -> rootFolder -> getUserFolder ( $share -> getShareOwner ());
} else {
$userFolder = $this -> rootFolder -> getUserFolder ( $share -> getSharedBy ());
}
2020-03-26 11:30:18 +03:00
\OC_Hook :: emit ( Share :: class , 'post_update_permissions' , [
2016-02-04 11:53:47 +03:00
'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 (),
2016-02-04 15:25:24 +03:00
'path' => $userFolder -> getRelativePath ( $share -> getNode () -> getPath ()),
2020-03-26 11:30:18 +03:00
]);
2016-02-04 11:53:47 +03:00
}
2016-01-25 15:09:25 +03:00
return $share ;
2015-10-30 15:09:07 +03:00
}
2019-09-04 17:50:52 +03:00
/**
* Accept a share .
*
* @ param IShare $share
* @ param string $recipientId
* @ return IShare The share object
* @ throws \InvalidArgumentException
* @ since 9.0 . 0
*/
public function acceptShare ( IShare $share , string $recipientId ) : IShare {
[ $providerId , ] = $this -> splitFullId ( $share -> getFullId ());
$provider = $this -> factory -> getProvider ( $providerId );
if ( ! method_exists ( $provider , 'acceptShare' )) {
// TODO FIX ME
2019-09-09 15:32:11 +03:00
throw new \InvalidArgumentException ( 'Share provider does not support accepting' );
2019-09-04 17:50:52 +03:00
}
$provider -> acceptShare ( $share , $recipientId );
$event = new GenericEvent ( $share );
2019-12-12 23:38:52 +03:00
$this -> legacyDispatcher -> dispatch ( 'OCP\Share::postAcceptShare' , $event );
2019-09-04 17:50:52 +03:00
return $share ;
}
2017-04-23 20:47:28 +03:00
/**
* Updates the password of the given share if it is not the same as the
* password of the original share .
*
* @ param \OCP\Share\IShare $share the share to update its password .
* @ param \OCP\Share\IShare $originalShare the original share to compare its
* password with .
* @ return boolean whether the password was updated or not .
*/
private function updateSharePasswordIfNeeded ( \OCP\Share\IShare $share , \OCP\Share\IShare $originalShare ) {
2020-05-28 21:37:18 +03:00
$passwordsAreDifferent = ( $share -> getPassword () !== $originalShare -> getPassword ()) &&
(( $share -> getPassword () !== null && $originalShare -> getPassword () === null ) ||
( $share -> getPassword () === null && $originalShare -> getPassword () !== null ) ||
( $share -> getPassword () !== null && $originalShare -> getPassword () !== null &&
! $this -> hasher -> verify ( $share -> getPassword (), $originalShare -> getPassword ())));
2017-04-23 20:47:28 +03:00
// Password updated.
2020-05-28 21:37:18 +03:00
if ( $passwordsAreDifferent ) {
2017-04-23 20:47:28 +03:00
//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 ()));
return true ;
}
2020-05-28 21:37:18 +03:00
} else {
// Reset the password to the original one, as it is either the same
// as the "new" password or a hashed version of it.
$share -> setPassword ( $originalShare -> getPassword ());
2017-04-23 20:47:28 +03:00
}
return false ;
}
2015-11-10 16:14:49 +03:00
/**
* Delete all the children of this share
2016-01-29 13:41:28 +03:00
* FIXME : remove once https :// github . com / owncloud / core / pull / 21660 is in
2015-11-10 16:14:49 +03:00
*
2016-01-27 14:13:53 +03:00
* @ param \OCP\Share\IShare $share
* @ return \OCP\Share\IShare [] List of deleted shares
2015-11-10 16:14:49 +03:00
*/
2016-01-27 14:13:53 +03:00
protected function deleteChildren ( \OCP\Share\IShare $share ) {
2015-11-10 16:14:49 +03:00
$deletedShares = [];
2016-01-13 16:21:55 +03:00
$provider = $this -> factory -> getProviderForType ( $share -> getShareType ());
2016-01-08 16:31:28 +03:00
2016-01-13 15:02:23 +03:00
foreach ( $provider -> getChildren ( $share ) as $child ) {
$deletedChildren = $this -> deleteChildren ( $child );
$deletedShares = array_merge ( $deletedShares , $deletedChildren );
2016-01-08 16:31:28 +03:00
2016-01-13 15:02:23 +03:00
$provider -> delete ( $child );
$deletedShares [] = $child ;
2015-11-10 16:14:49 +03:00
}
return $deletedShares ;
}
2015-10-30 15:09:07 +03:00
/**
* Delete a share
*
2016-01-27 14:13:53 +03:00
* @ param \OCP\Share\IShare $share
2015-10-30 15:10:08 +03:00
* @ throws ShareNotFound
2016-02-08 23:08:03 +03:00
* @ throws \InvalidArgumentException
2015-10-30 15:09:07 +03:00
*/
2016-01-27 14:13:53 +03:00
public function deleteShare ( \OCP\Share\IShare $share ) {
2016-02-08 23:08:03 +03:00
try {
2016-02-11 12:51:58 +03:00
$share -> getFullId ();
2016-02-08 23:08:03 +03:00
} catch ( \UnexpectedValueException $e ) {
throw new \InvalidArgumentException ( 'Share does not have a full id' );
}
2015-11-10 16:14:49 +03:00
2017-03-14 17:12:16 +03:00
$event = new GenericEvent ( $share );
2019-12-12 23:38:52 +03:00
$this -> legacyDispatcher -> dispatch ( 'OCP\Share::preUnshare' , $event );
2015-11-10 16:14:49 +03:00
// Get all children and delete them as well
$deletedShares = $this -> deleteChildren ( $share );
// Do the actual delete
2016-01-13 16:21:55 +03:00
$provider = $this -> factory -> getProviderForType ( $share -> getShareType ());
2016-01-08 16:31:28 +03:00
$provider -> delete ( $share );
2015-11-10 16:14:49 +03:00
// All the deleted shares caused by this delete
$deletedShares [] = $share ;
// Emit post hook
2017-03-14 17:12:16 +03:00
$event -> setArgument ( 'deletedShares' , $deletedShares );
2019-12-12 23:38:52 +03:00
$this -> legacyDispatcher -> dispatch ( 'OCP\Share::postUnshare' , $event );
2015-10-30 15:09:07 +03:00
}
2015-12-03 12:51:41 +03:00
2016-01-21 16:31:09 +03:00
/**
* 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 .
*
2016-01-27 14:13:53 +03:00
* @ param \OCP\Share\IShare $share
2016-02-02 23:02:09 +03:00
* @ param string $recipientId
2016-01-21 16:31:09 +03:00
*/
2016-02-02 23:02:09 +03:00
public function deleteFromSelf ( \OCP\Share\IShare $share , $recipientId ) {
2016-04-15 10:02:01 +03:00
list ( $providerId , ) = $this -> splitFullId ( $share -> getFullId ());
2016-01-21 16:31:09 +03:00
$provider = $this -> factory -> getProvider ( $providerId );
2016-02-02 23:02:09 +03:00
$provider -> deleteFromSelf ( $share , $recipientId );
2017-07-16 17:26:11 +03:00
$event = new GenericEvent ( $share );
2019-12-12 23:38:52 +03:00
$this -> legacyDispatcher -> dispatch ( 'OCP\Share::postUnshareFromSelf' , $event );
2016-01-21 16:31:09 +03:00
}
2018-06-19 10:20:35 +03:00
public function restoreShare ( IShare $share , string $recipientId ) : IShare {
list ( $providerId , ) = $this -> splitFullId ( $share -> getFullId ());
$provider = $this -> factory -> getProvider ( $providerId );
return $provider -> restore ( $share , $recipientId );
}
2016-01-29 12:27:39 +03:00
/**
* @ inheritdoc
*/
2016-02-02 23:02:09 +03:00
public function moveShare ( \OCP\Share\IShare $share , $recipientId ) {
2016-01-29 12:27:39 +03:00
if ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_LINK ) {
2017-05-17 16:35:10 +03:00
throw new \InvalidArgumentException ( 'Can’ t change target of link share' );
2016-01-29 12:27:39 +03:00
}
2016-02-02 23:02:09 +03:00
if ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_USER && $share -> getSharedWith () !== $recipientId ) {
2016-01-29 12:27:39 +03:00
throw new \InvalidArgumentException ( 'Invalid recipient' );
}
2016-02-02 23:02:09 +03:00
if ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_GROUP ) {
$sharedWith = $this -> groupManager -> get ( $share -> getSharedWith ());
2017-01-19 17:02:46 +03:00
if ( is_null ( $sharedWith )) {
throw new \InvalidArgumentException ( 'Group "' . $share -> getSharedWith () . '" does not exist' );
}
2016-02-02 23:02:09 +03:00
$recipient = $this -> userManager -> get ( $recipientId );
if ( ! $sharedWith -> inGroup ( $recipient )) {
throw new \InvalidArgumentException ( 'Invalid recipient' );
}
}
2016-04-15 10:02:01 +03:00
list ( $providerId , ) = $this -> splitFullId ( $share -> getFullId ());
2016-01-29 12:27:39 +03:00
$provider = $this -> factory -> getProvider ( $providerId );
2016-02-02 23:02:09 +03:00
$provider -> move ( $share , $recipientId );
2016-01-29 12:27:39 +03:00
}
2016-10-31 22:19:00 +03:00
public function getSharesInFolder ( $userId , Folder $node , $reshares = false ) {
2016-06-18 23:04:56 +03:00
$providers = $this -> factory -> getAllProviders ();
2020-04-09 14:53:40 +03:00
return array_reduce ( $providers , function ( $shares , IShareProvider $provider ) use ( $userId , $node , $reshares ) {
2016-11-01 14:15:14 +03:00
$newShares = $provider -> getSharesInFolder ( $userId , $node , $reshares );
foreach ( $newShares as $fid => $data ) {
if ( ! isset ( $shares [ $fid ])) {
$shares [ $fid ] = [];
}
$shares [ $fid ] = array_merge ( $shares [ $fid ], $data );
}
return $shares ;
2016-06-18 23:04:56 +03:00
}, []);
}
2015-12-03 12:51:41 +03:00
/**
2016-03-11 10:51:07 +03:00
* @ inheritdoc
2015-12-03 12:51:41 +03:00
*/
2016-02-02 23:02:09 +03:00
public function getSharesBy ( $userId , $shareType , $path = null , $reshares = false , $limit = 50 , $offset = 0 ) {
2015-12-03 12:51:41 +03:00
if ( $path !== null &&
! ( $path instanceof \OCP\Files\File ) &&
! ( $path instanceof \OCP\Files\Folder )) {
throw new \InvalidArgumentException ( 'invalid path' );
}
2016-12-05 21:40:37 +03:00
try {
$provider = $this -> factory -> getProviderForType ( $shareType );
} catch ( ProviderException $e ) {
return [];
}
2015-12-03 12:51:41 +03:00
2016-02-05 22:00:07 +03:00
$shares = $provider -> getSharesBy ( $userId , $shareType , $path , $reshares , $limit , $offset );
/*
* Work around so we don ' t return expired shares but still follow
* proper pagination .
*/
2017-03-30 18:03:04 +03:00
$shares2 = [];
2017-03-29 17:50:23 +03:00
2020-04-10 15:19:56 +03:00
while ( true ) {
2017-03-30 18:03:04 +03:00
$added = 0 ;
foreach ( $shares as $share ) {
try {
$this -> checkExpireDate ( $share );
} catch ( ShareNotFound $e ) {
//Ignore since this basically means the share is deleted
continue ;
2016-02-05 22:00:07 +03:00
}
2017-03-30 18:03:04 +03:00
$added ++ ;
$shares2 [] = $share ;
2016-02-05 22:00:07 +03:00
2017-03-30 18:03:04 +03:00
if ( count ( $shares2 ) === $limit ) {
2016-03-08 18:12:17 +03:00
break ;
}
2017-03-30 18:03:04 +03:00
}
2016-03-08 18:12:17 +03:00
2017-09-13 22:42:47 +03:00
// If we did not fetch more shares than the limit then there are no more shares
if ( count ( $shares ) < $limit ) {
break ;
}
2017-03-30 18:03:04 +03:00
if ( count ( $shares2 ) === $limit ) {
break ;
}
2016-02-05 22:00:07 +03:00
2017-03-30 18:03:04 +03:00
// If there was no limit on the select we are done
if ( $limit === - 1 ) {
break ;
2016-02-05 22:00:07 +03:00
}
2017-03-30 18:03:04 +03:00
$offset += $added ;
2016-02-05 22:00:07 +03:00
2017-03-30 18:03:04 +03:00
// Fetch again $limit shares
$shares = $provider -> getSharesBy ( $userId , $shareType , $path , $reshares , $limit , $offset );
2017-03-29 17:50:23 +03:00
2017-03-30 18:03:04 +03:00
// No more shares means we are done
if ( empty ( $shares )) {
break ;
2017-03-29 17:50:23 +03:00
}
}
2017-03-30 18:03:04 +03:00
$shares = $shares2 ;
2017-03-29 17:50:23 +03:00
2016-02-05 22:00:07 +03:00
return $shares ;
2015-12-03 12:51:41 +03:00
}
2015-10-30 15:09:07 +03:00
/**
2016-01-29 17:26:04 +03:00
* @ inheritdoc
2015-10-30 15:09:07 +03:00
*/
2016-02-02 23:02:09 +03:00
public function getSharedWith ( $userId , $shareType , $node = null , $limit = 50 , $offset = 0 ) {
2016-12-05 21:40:37 +03:00
try {
$provider = $this -> factory -> getProviderForType ( $shareType );
} catch ( ProviderException $e ) {
return [];
}
2015-12-03 12:51:41 +03:00
2017-03-29 17:50:23 +03:00
$shares = $provider -> getSharedWith ( $userId , $shareType , $node , $limit , $offset );
// remove all shares which are already expired
foreach ( $shares as $key => $share ) {
try {
$this -> checkExpireDate ( $share );
} catch ( ShareNotFound $e ) {
unset ( $shares [ $key ]);
}
}
return $shares ;
2015-10-30 15:09:07 +03:00
}
2018-06-19 18:44:20 +03:00
/**
* @ inheritdoc
*/
public function getDeletedSharedWith ( $userId , $shareType , $node = null , $limit = 50 , $offset = 0 ) {
$shares = $this -> getSharedWith ( $userId , $shareType , $node , $limit , $offset );
// Only get deleted shares
2020-04-09 14:53:40 +03:00
$shares = array_filter ( $shares , function ( IShare $share ) {
2018-06-19 18:44:20 +03:00
return $share -> getPermissions () === 0 ;
});
// Only get shares where the owner still exists
$shares = array_filter ( $shares , function ( IShare $share ) {
return $this -> userManager -> userExists ( $share -> getShareOwner ());
});
return $shares ;
}
2015-10-30 15:09:07 +03:00
/**
2016-01-29 12:07:28 +03:00
* @ inheritdoc
2015-10-30 15:09:07 +03:00
*/
2016-01-29 12:07:28 +03:00
public function getShareById ( $id , $recipient = null ) {
2015-11-10 16:14:49 +03:00
if ( $id === null ) {
throw new ShareNotFound ();
}
2016-01-13 15:02:23 +03:00
list ( $providerId , $id ) = $this -> splitFullId ( $id );
2016-12-05 21:40:37 +03:00
try {
$provider = $this -> factory -> getProvider ( $providerId );
} catch ( ProviderException $e ) {
throw new ShareNotFound ();
}
2016-01-08 16:31:28 +03:00
2016-01-29 12:07:28 +03:00
$share = $provider -> getShareById ( $id , $recipient );
2015-10-30 15:10:08 +03:00
2017-03-29 17:50:23 +03:00
$this -> checkExpireDate ( $share );
2016-02-05 17:34:30 +03:00
2015-10-30 15:10:08 +03:00
return $share ;
2015-10-30 15:09:07 +03:00
}
/**
* 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 ) {
2016-12-07 09:11:32 +03:00
return [];
2015-10-30 15:09:07 +03:00
}
/**
* Get the share by token possible with password
*
* @ param string $token
* @ return Share
*
2015-11-23 16:06:25 +03:00
* @ throws ShareNotFound
2015-10-30 15:09:07 +03:00
*/
2016-01-14 18:44:59 +03:00
public function getShareByToken ( $token ) {
2018-08-16 21:39:51 +03:00
// tokens can't be valid local user names
if ( $this -> userManager -> userExists ( $token )) {
throw new ShareNotFound ();
}
2016-12-07 09:11:32 +03:00
$share = null ;
2016-12-05 21:40:37 +03:00
try {
2020-04-10 15:19:56 +03:00
if ( $this -> shareApiAllowLinks ()) {
2016-12-22 12:59:27 +03:00
$provider = $this -> factory -> getProviderForType ( \OCP\Share :: SHARE_TYPE_LINK );
$share = $provider -> getShareByToken ( $token );
2016-12-20 10:52:31 +03:00
}
2016-12-07 09:11:32 +03:00
} catch ( ProviderException $e ) {
2016-04-01 18:02:59 +03:00
} catch ( ShareNotFound $e ) {
}
2016-12-07 09:11:32 +03:00
2016-04-01 18:02:59 +03:00
// If it is not a link share try to fetch a federated share by token
if ( $share === null ) {
2016-12-05 21:40:37 +03:00
try {
$provider = $this -> factory -> getProviderForType ( \OCP\Share :: SHARE_TYPE_REMOTE );
2016-10-26 13:18:39 +03:00
$share = $provider -> getShareByToken ( $token );
2016-12-07 09:11:32 +03:00
} catch ( ProviderException $e ) {
2016-10-26 13:18:39 +03:00
} catch ( ShareNotFound $e ) {
}
}
2016-12-22 12:59:27 +03:00
// If it is not a link share try to fetch a mail share by token
2016-10-26 13:18:39 +03:00
if ( $share === null && $this -> shareProviderExists ( \OCP\Share :: SHARE_TYPE_EMAIL )) {
2016-12-05 21:40:37 +03:00
try {
$provider = $this -> factory -> getProviderForType ( \OCP\Share :: SHARE_TYPE_EMAIL );
2016-12-07 09:11:32 +03:00
$share = $provider -> getShareByToken ( $token );
2016-12-05 21:40:37 +03:00
} catch ( ProviderException $e ) {
2016-12-07 09:11:32 +03:00
} catch ( ShareNotFound $e ) {
2016-12-05 21:40:37 +03:00
}
2016-12-07 09:11:32 +03:00
}
2017-08-20 16:21:58 +03:00
if ( $share === null && $this -> shareProviderExists ( \OCP\Share :: SHARE_TYPE_CIRCLE )) {
try {
$provider = $this -> factory -> getProviderForType ( \OCP\Share :: SHARE_TYPE_CIRCLE );
$share = $provider -> getShareByToken ( $token );
} catch ( ProviderException $e ) {
} catch ( ShareNotFound $e ) {
}
}
2018-08-01 20:04:32 +03:00
if ( $share === null && $this -> shareProviderExists ( \OCP\Share :: SHARE_TYPE_ROOM )) {
try {
$provider = $this -> factory -> getProviderForType ( \OCP\Share :: SHARE_TYPE_ROOM );
$share = $provider -> getShareByToken ( $token );
} catch ( ProviderException $e ) {
} catch ( ShareNotFound $e ) {
}
}
2016-12-07 09:11:32 +03:00
if ( $share === null ) {
2017-06-13 19:44:14 +03:00
throw new ShareNotFound ( $this -> l -> t ( 'The requested share does not exist anymore' ));
2016-04-01 18:02:59 +03:00
}
2016-01-14 18:44:59 +03:00
2017-03-29 17:50:23 +03:00
$this -> checkExpireDate ( $share );
2016-01-14 18:44:59 +03:00
2016-04-01 18:02:59 +03:00
/*
* Reduce the permissions for link shares if public upload is not enabled
*/
if ( $share -> getShareType () === \OCP\Share :: SHARE_TYPE_LINK &&
! $this -> shareApiLinkAllowPublicUpload ()) {
$share -> setPermissions ( $share -> getPermissions () & ~ ( \OCP\Constants :: PERMISSION_CREATE | \OCP\Constants :: PERMISSION_UPDATE ));
}
2016-01-14 18:44:59 +03:00
return $share ;
}
2017-03-29 17:50:23 +03:00
protected function checkExpireDate ( $share ) {
2019-08-12 11:56:23 +03:00
if ( $share -> isExpired ()) {
2017-03-29 17:50:23 +03:00
$this -> deleteShare ( $share );
2017-06-13 19:44:14 +03:00
throw new ShareNotFound ( $this -> l -> t ( 'The requested share does not exist anymore' ));
2017-03-29 17:50:23 +03:00
}
}
2016-01-14 18:44:59 +03:00
/**
* Verify the password of a public share
*
2016-01-27 14:13:53 +03:00
* @ param \OCP\Share\IShare $share
2016-01-14 18:44:59 +03:00
* @ param string $password
* @ return bool
*/
2016-01-27 14:13:53 +03:00
public function checkPassword ( \OCP\Share\IShare $share , $password ) {
2019-12-05 02:11:18 +03:00
$passwordProtected = $share -> getShareType () !== IShare :: TYPE_LINK
|| $share -> getShareType () !== IShare :: TYPE_EMAIL
|| $share -> getShareType () !== IShare :: TYPE_CIRCLE ;
2017-03-28 15:39:38 +03:00
if ( ! $passwordProtected ) {
2016-01-14 18:44:59 +03:00
//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 )) {
2016-02-01 23:46:32 +03:00
$share -> setPassword ( $newHash );
$provider = $this -> factory -> getProviderForType ( $share -> getShareType ());
$provider -> update ( $share );
2016-01-14 18:44:59 +03:00
}
return true ;
2015-10-30 15:09:07 +03:00
}
2016-04-04 13:28:19 +03:00
/**
* @ inheritdoc
*/
public function userDeleted ( $uid ) {
2016-12-05 21:40:37 +03:00
$types = [ \OCP\Share :: SHARE_TYPE_USER , \OCP\Share :: SHARE_TYPE_GROUP , \OCP\Share :: SHARE_TYPE_LINK , \OCP\Share :: SHARE_TYPE_REMOTE , \OCP\Share :: SHARE_TYPE_EMAIL ];
2016-04-04 13:28:19 +03:00
foreach ( $types as $type ) {
2016-12-05 21:40:37 +03:00
try {
$provider = $this -> factory -> getProviderForType ( $type );
} catch ( ProviderException $e ) {
continue ;
}
2016-04-04 13:28:19 +03:00
$provider -> userDeleted ( $uid , $type );
}
}
2016-04-12 10:46:25 +03:00
/**
* @ inheritdoc
*/
public function groupDeleted ( $gid ) {
$provider = $this -> factory -> getProviderForType ( \OCP\Share :: SHARE_TYPE_GROUP );
$provider -> groupDeleted ( $gid );
}
2016-04-13 16:00:12 +03:00
/**
* @ inheritdoc
*/
public function userDeletedFromGroup ( $uid , $gid ) {
$provider = $this -> factory -> getProviderForType ( \OCP\Share :: SHARE_TYPE_GROUP );
$provider -> userDeletedFromGroup ( $uid , $gid );
}
2015-10-30 15:09:07 +03:00
/**
* Get access list to a path . This means
2016-09-09 13:53:17 +03:00
* all the users that can access a given path .
2015-10-30 15:09:07 +03:00
*
* Consider :
* - root
2017-04-11 13:40:36 +03:00
* |- folder1 ( 23 )
* |- folder2 ( 32 )
* |- fileA ( 42 )
2015-10-30 15:09:07 +03:00
*
2017-04-11 13:40:36 +03:00
* fileA is shared with user1 and user1 @ server1
2016-09-09 13:53:17 +03:00
* folder2 is shared with group2 ( user4 is a member of group2 )
2017-04-11 13:40:36 +03:00
* folder1 is shared with user2 ( renamed to " folder (1) " ) and user2 @ server2
2015-10-30 15:09:07 +03:00
*
2017-04-11 13:40:36 +03:00
* Then the access list to '/folder1/folder2/fileA' with $currentAccess is :
2015-10-30 15:09:07 +03:00
* [
2017-04-11 13:40:36 +03:00
* users => [
* 'user1' => [ 'node_id' => 42 , 'node_path' => '/fileA' ],
* 'user4' => [ 'node_id' => 32 , 'node_path' => '/folder2' ],
* 'user2' => [ 'node_id' => 23 , 'node_path' => '/folder (1)' ],
* ],
* remote => [
* 'user1@server1' => [ 'node_id' => 42 , 'token' => 'SeCr3t' ],
* 'user2@server2' => [ 'node_id' => 23 , 'token' => 'FooBaR' ],
* ],
* public => bool
* mail => bool
* ]
*
* The access list to '/folder1/folder2/fileA' ** without ** $currentAccess is :
* [
* users => [ 'user1' , 'user2' , 'user4' ],
* remote => bool ,
2016-09-09 13:53:17 +03:00
* public => bool
2017-03-27 17:06:31 +03:00
* mail => bool
2015-10-30 15:09:07 +03:00
* ]
*
2016-10-19 22:27:07 +03:00
* This is required for encryption / activity
2015-10-30 15:09:07 +03:00
*
* @ param \OCP\Files\Node $path
2016-10-19 22:27:07 +03:00
* @ param bool $recursive Should we check all parent folders as well
2017-08-30 11:56:02 +03:00
* @ param bool $currentAccess Ensure the recipient has access to the file ( e . g . did not unshare it )
2016-09-09 13:53:17 +03:00
* @ return array
2015-10-30 15:09:07 +03:00
*/
2016-12-22 23:44:21 +03:00
public function getAccessList ( \OCP\Files\Node $path , $recursive = true , $currentAccess = false ) {
2019-03-19 17:38:52 +03:00
$owner = $path -> getOwner ();
if ( $owner === null ) {
return [];
}
$owner = $owner -> getUID ();
2016-09-09 13:53:17 +03:00
2017-04-11 13:40:36 +03:00
if ( $currentAccess ) {
$al = [ 'users' => [], 'remote' => [], 'public' => false ];
} else {
$al = [ 'users' => [], 'remote' => false , 'public' => false ];
}
2016-12-22 23:44:21 +03:00
if ( ! $this -> userManager -> userExists ( $owner )) {
return $al ;
}
2019-02-14 12:59:17 +03:00
//Get node for the owner and correct the owner in case of external storages
2016-09-09 13:53:17 +03:00
$userFolder = $this -> rootFolder -> getUserFolder ( $owner );
2017-04-19 16:11:54 +03:00
if ( $path -> getId () !== $userFolder -> getId () && ! $userFolder -> isSubNode ( $path )) {
2019-02-14 12:59:17 +03:00
$nodes = $userFolder -> getById ( $path -> getId ());
$path = array_shift ( $nodes );
2019-06-01 00:16:06 +03:00
if ( $path -> getOwner () === null ) {
return [];
}
2019-02-14 12:59:17 +03:00
$owner = $path -> getOwner () -> getUID ();
2016-10-19 22:27:07 +03:00
}
2016-09-09 13:53:17 +03:00
$providers = $this -> factory -> getAllProviders ();
2016-12-22 23:44:21 +03:00
/** @var Node[] $nodes */
$nodes = [];
2017-04-13 13:58:29 +03:00
if ( $currentAccess ) {
$ownerPath = $path -> getPath ();
2017-04-19 16:11:54 +03:00
$ownerPath = explode ( '/' , $ownerPath , 4 );
if ( count ( $ownerPath ) < 4 ) {
$ownerPath = '' ;
} else {
$ownerPath = $ownerPath [ 3 ];
}
2017-04-13 13:58:29 +03:00
$al [ 'users' ][ $owner ] = [
'node_id' => $path -> getId (),
'node_path' => '/' . $ownerPath ,
];
} else {
$al [ 'users' ][] = $owner ;
}
2016-09-09 13:53:17 +03:00
// Collect all the shares
2016-10-19 22:27:07 +03:00
while ( $path -> getPath () !== $userFolder -> getPath ()) {
2016-12-22 23:44:21 +03:00
$nodes [] = $path ;
2016-10-19 22:27:07 +03:00
if ( ! $recursive ) {
break ;
}
2016-09-09 13:53:17 +03:00
$path = $path -> getParent ();
}
2016-12-22 23:44:21 +03:00
foreach ( $providers as $provider ) {
$tmp = $provider -> getAccessList ( $nodes , $currentAccess );
2016-09-09 13:53:17 +03:00
2017-01-04 10:59:43 +03:00
foreach ( $tmp as $k => $v ) {
if ( isset ( $al [ $k ])) {
if ( is_array ( $al [ $k ])) {
2017-11-28 18:38:16 +03:00
if ( $currentAccess ) {
$al [ $k ] += $v ;
} else {
$al [ $k ] = array_merge ( $al [ $k ], $v );
$al [ $k ] = array_unique ( $al [ $k ]);
$al [ $k ] = array_values ( $al [ $k ]);
}
2017-01-04 10:59:43 +03:00
} else {
$al [ $k ] = $al [ $k ] || $v ;
}
} else {
$al [ $k ] = $v ;
}
}
2016-09-09 13:53:17 +03:00
}
2016-12-22 23:44:21 +03:00
return $al ;
2015-10-30 15:09:07 +03:00
}
2015-12-15 11:54:12 +03:00
/**
* Create a new share
2017-10-18 15:15:03 +03:00
* @ return \OCP\Share\IShare
2015-12-15 11:54:12 +03:00
*/
public function newShare () {
2016-05-11 21:48:27 +03:00
return new \OC\Share20\Share ( $this -> rootFolder , $this -> userManager );
2015-12-15 11:54:12 +03:00
}
/**
* 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' ;
}
/**
2019-10-28 21:22:05 +03:00
* Is default link expire date enabled
2015-12-15 11:54:12 +03:00
*
* @ return bool
*/
public function shareApiLinkDefaultExpireDate () {
return $this -> config -> getAppValue ( 'core' , 'shareapi_default_expire_date' , 'no' ) === 'yes' ;
}
/**
2019-10-28 21:22:05 +03:00
* Is default link expire date enforced
2015-12-15 11:54:12 +03:00
* `
* @ return bool
*/
public function shareApiLinkDefaultExpireDateEnforced () {
2016-02-26 13:38:06 +03:00
return $this -> shareApiLinkDefaultExpireDate () &&
$this -> config -> getAppValue ( 'core' , 'shareapi_enforce_expire_date' , 'no' ) === 'yes' ;
2015-12-15 11:54:12 +03:00
}
2019-10-28 21:22:05 +03:00
2015-12-15 11:54:12 +03:00
/**
2019-10-28 21:22:05 +03:00
* Number of default link expire days
2015-12-15 11:54:12 +03:00
* @ return int
*/
public function shareApiLinkDefaultExpireDays () {
return ( int ) $this -> config -> getAppValue ( 'core' , 'shareapi_expire_after_n_days' , '7' );
}
2019-10-28 21:22:05 +03:00
/**
* Is default internal expire date enabled
*
* @ return bool
*/
public function shareApiInternalDefaultExpireDate () : bool {
return $this -> config -> getAppValue ( 'core' , 'shareapi_default_internal_expire_date' , 'no' ) === 'yes' ;
}
/**
* Is default expire date enforced
* `
* @ return bool
*/
public function shareApiInternalDefaultExpireDateEnforced () : bool {
2019-10-29 23:10:50 +03:00
return $this -> shareApiInternalDefaultExpireDate () &&
2019-10-28 21:22:05 +03:00
$this -> config -> getAppValue ( 'core' , 'shareapi_enforce_internal_expire_date' , 'no' ) === 'yes' ;
}
/**
* Number of default expire days
* @ return int
*/
public function shareApiInternalDefaultExpireDays () : int {
return ( int ) $this -> config -> getAppValue ( 'core' , 'shareapi_internal_expire_after_n_days' , '7' );
}
2015-12-15 11:54:12 +03:00
/**
* 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' ;
}
2016-03-18 18:34:23 +03:00
/**
* Check if users can share with groups
* @ return bool
*/
public function allowGroupSharing () {
return $this -> config -> getAppValue ( 'core' , 'shareapi_allow_group_sharing' , 'yes' ) === 'yes' ;
}
2015-12-15 11:54:12 +03:00
2020-02-20 20:51:35 +03:00
public function allowEnumeration () : bool {
return $this -> config -> getAppValue ( 'core' , 'shareapi_allow_share_dialog_user_enumeration' , 'yes' ) === 'yes' ;
}
public function limitEnumerationToGroups () : bool {
return $this -> allowEnumeration () &&
$this -> config -> getAppValue ( 'core' , 'shareapi_restrict_user_enumeration_to_group' , 'no' ) === 'yes' ;
}
2015-12-15 11:54:12 +03:00
/**
* Copied from \OC_Util :: isSharingDisabledForUser
*
* TODO : Deprecate fuction from OC_Util
*
2016-02-02 23:02:09 +03:00
* @ param string $userId
2015-12-15 11:54:12 +03:00
* @ return bool
*/
2016-02-02 23:02:09 +03:00
public function sharingDisabledForUser ( $userId ) {
2016-05-03 15:05:51 +03:00
if ( $userId === null ) {
return false ;
}
2016-05-03 12:58:33 +03:00
if ( isset ( $this -> sharingDisabledForUsersCache [ $userId ])) {
return $this -> sharingDisabledForUsersCache [ $userId ];
}
2015-12-15 11:54:12 +03:00
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 );
}
2016-02-02 23:02:09 +03:00
$user = $this -> userManager -> get ( $userId );
2015-12-15 11:54:12 +03:00
$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 )) {
2016-05-03 12:58:33 +03:00
$this -> sharingDisabledForUsersCache [ $userId ] = true ;
2015-12-15 11:54:12 +03:00
return true ;
}
}
}
2016-05-03 12:58:33 +03:00
$this -> sharingDisabledForUsersCache [ $userId ] = false ;
2015-12-15 11:54:12 +03:00
return false ;
}
2016-02-04 13:13:06 +03:00
/**
* @ inheritdoc
*/
public function outgoingServer2ServerSharesAllowed () {
return $this -> config -> getAppValue ( 'files_sharing' , 'outgoing_server2server_share_enabled' , 'yes' ) === 'yes' ;
}
2018-06-13 15:19:59 +03:00
/**
* @ inheritdoc
*/
public function outgoingServer2ServerGroupSharesAllowed () {
return $this -> config -> getAppValue ( 'files_sharing' , 'outgoing_server2server_group_share_enabled' , 'no' ) === 'yes' ;
}
2016-10-24 18:03:36 +03:00
/**
* @ inheritdoc
*/
public function shareProviderExists ( $shareType ) {
try {
$this -> factory -> getProviderForType ( $shareType );
} catch ( ProviderException $e ) {
return false ;
}
return true ;
}
2019-10-29 18:56:06 +03:00
public function getAllShares () : iterable {
$providers = $this -> factory -> getAllProviders ();
foreach ( $providers as $provider ) {
yield from $provider -> getAllShares ();
}
}
2015-10-30 15:09:07 +03:00
}