2015-10-30 08:09:07 -04:00
< ? php
2024-05-23 03:26:56 -04:00
2015-10-30 08:09:07 -04:00
/**
2024-05-23 03:26:56 -04:00
* SPDX - FileCopyrightText : 2016 - 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX - FileCopyrightText : 2016 ownCloud , Inc .
* SPDX - License - Identifier : AGPL - 3.0 - only
2015-10-30 08:09:07 -04:00
*/
namespace OC\Share20 ;
2016-03-30 07:33:16 -04:00
use OC\Files\Mount\MoveableMount ;
2021-11-05 05:44:51 -04:00
use OC\KnownUser\KnownUserService ;
2016-10-24 11:03:36 -04:00
use OC\Share20\Exception\ProviderException ;
2021-07-25 11:57:11 -04:00
use OCA\Files_Sharing\AppInfo\Application ;
2024-08-05 08:51:14 -04:00
use OCA\Files_Sharing\SharedStorage ;
2019-12-12 15:38:52 -05:00
use OCP\EventDispatcher\IEventDispatcher ;
2016-05-03 05:58:33 -04:00
use OCP\Files\File ;
use OCP\Files\Folder ;
2016-02-04 06:51:23 -05:00
use OCP\Files\IRootFolder ;
2016-05-03 05:58:33 -04:00
use OCP\Files\Mount\IMountManager ;
2024-10-22 07:19:18 -04:00
use OCP\Files\Mount\IShareOwnerlessMount ;
2016-06-18 16:04:56 -04:00
use OCP\Files\Node ;
2024-08-07 05:13:35 -04:00
use OCP\Files\NotFoundException ;
2021-06-29 19:20:33 -04:00
use OCP\HintException ;
2024-12-16 18:53:12 -05:00
use OCP\IAppConfig ;
2015-12-15 03:54:12 -05:00
use OCP\IConfig ;
2024-02-07 06:06:12 -05:00
use OCP\IDateTimeZone ;
2016-05-03 05:58:33 -04:00
use OCP\IGroupManager ;
2016-01-05 06:50:00 -05:00
use OCP\IL10N ;
2017-07-26 17:53:07 -04:00
use OCP\IURLGenerator ;
2017-08-01 11:50:57 -04:00
use OCP\IUser ;
2016-05-03 05:58:33 -04:00
use OCP\IUserManager ;
2021-04-23 14:20:56 -04:00
use OCP\IUserSession ;
2017-09-08 04:34:19 -04:00
use OCP\L10N\IFactory ;
2017-07-26 17:53:07 -04:00
use OCP\Mail\IMailer ;
2019-11-19 13:18:00 -05:00
use OCP\Security\Events\ValidatePasswordPolicyEvent ;
2015-12-15 03:54:12 -05:00
use OCP\Security\IHasher ;
2016-05-03 05:58:33 -04:00
use OCP\Security\ISecureRandom ;
2025-01-18 06:17:18 -05:00
use OCP\Security\PasswordContext ;
2019-11-22 14:52:10 -05:00
use OCP\Share ;
2023-07-27 16:25:26 -04:00
use OCP\Share\Events\BeforeShareDeletedEvent ;
2023-07-27 16:36:13 -04:00
use OCP\Share\Events\ShareAcceptedEvent ;
2023-07-27 15:45:31 -04:00
use OCP\Share\Events\ShareCreatedEvent ;
2023-07-27 16:25:26 -04:00
use OCP\Share\Events\ShareDeletedEvent ;
use OCP\Share\Events\ShareDeletedFromSelfEvent ;
2021-02-08 10:02:08 -05:00
use OCP\Share\Exceptions\AlreadySharedException ;
2016-02-02 08:18:59 -05:00
use OCP\Share\Exceptions\GenericShareException ;
2016-05-03 05:58:33 -04:00
use OCP\Share\Exceptions\ShareNotFound ;
2024-12-16 18:53:12 -05:00
use OCP\Share\Exceptions\ShareTokenException ;
2016-05-03 05:58:33 -04:00
use OCP\Share\IManager ;
use OCP\Share\IProviderFactory ;
2018-06-19 03:20:35 -04:00
use OCP\Share\IShare ;
2019-11-22 14:52:10 -05:00
use OCP\Share\IShareProvider ;
2024-06-25 06:40:27 -04:00
use OCP\Share\IShareProviderSupportsAccept ;
2024-07-05 03:47:40 -04:00
use OCP\Share\IShareProviderWithNotification ;
2022-03-17 11:42:53 -04:00
use Psr\Log\LoggerInterface ;
2015-10-30 08:09:07 -04:00
/**
* This class is the communication hub for all sharing related operations .
*/
2016-01-27 06:31:10 -05:00
class Manager implements IManager {
2024-04-30 07:19:22 -04:00
private ? IL10N $l ;
private LegacyHooks $legacyHooks ;
2016-05-03 05:58:33 -04:00
2015-11-23 08:06:25 -05:00
public function __construct (
2024-04-30 07:19:22 -04:00
private LoggerInterface $logger ,
private IConfig $config ,
private ISecureRandom $secureRandom ,
private IHasher $hasher ,
private IMountManager $mountManager ,
private IGroupManager $groupManager ,
private IFactory $l10nFactory ,
private IProviderFactory $factory ,
private IUserManager $userManager ,
private IRootFolder $rootFolder ,
private IMailer $mailer ,
private IURLGenerator $urlGenerator ,
private \OC_Defaults $defaults ,
private IEventDispatcher $dispatcher ,
private IUserSession $userSession ,
private KnownUserService $knownUserService ,
private ShareDisableChecker $shareDisableChecker ,
private IDateTimeZone $dateTimeZone ,
2024-12-16 18:53:12 -05:00
private IAppConfig $appConfig ,
2015-11-23 08:06:25 -05:00
) {
2024-04-30 07:19:22 -04:00
$this -> l = $this -> l10nFactory -> get ( 'lib' );
2021-06-15 11:53:13 -04:00
// The constructor of LegacyHooks registers the listeners of share events
// do not remove if those are not properly migrated
2024-04-30 07:19:22 -04:00
$this -> legacyHooks = new LegacyHooks ( $this -> dispatcher );
2016-01-08 08:31:28 -05:00
}
2015-10-30 08:09:07 -04:00
2016-01-13 07:02:23 -05: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 03:54:12 -05:00
/**
* Verify if a password meets all requirements
*
* @ param string $password
2024-11-19 05:38:29 -05:00
* @ throws HintException
2015-12-15 03:54:12 -05:00
*/
protected function verifyPassword ( $password ) {
if ( $password === null ) {
// No password is set, check if this is allowed.
if ( $this -> shareApiLinkEnforcePassword ()) {
2023-08-30 09:22:45 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Passwords are enforced for link and mail shares' ));
2015-12-15 03:54:12 -05:00
}
return ;
}
2016-06-27 04:46:39 -04:00
// Let others verify the password
2024-12-10 09:27:38 -05:00
try {
2025-01-18 06:17:18 -05:00
$event = new ValidatePasswordPolicyEvent ( $password , PasswordContext :: SHARING );
$this -> dispatcher -> dispatchTyped ( $event );
2024-12-10 09:27:38 -05:00
} catch ( HintException $e ) {
/* Wrap in a 400 bad request error */
throw new HintException ( $e -> getMessage (), $e -> getHint (), 400 , $e );
}
2015-12-15 03:54:12 -05:00
}
/**
* Check for generic requirements before creating a share
*
2020-06-24 10:49:16 -04:00
* @ param IShare $share
2016-02-02 08:18:59 -05:00
* @ throws \InvalidArgumentException
* @ throws GenericShareException
2017-07-18 14:38:40 -04:00
*
* @ suppress PhanUndeclaredClassMethod
2015-12-15 03:54:12 -05:00
*/
2024-03-19 21:30:00 -04:00
protected function generalCreateChecks ( IShare $share , bool $isUpdate = false ) {
2020-06-24 10:49:16 -04:00
if ( $share -> getShareType () === IShare :: TYPE_USER ) {
2015-12-15 03:54:12 -05:00
// We expect a valid user as sharedWith for user shares
2016-02-02 15:02:09 -05:00
if ( ! $this -> userManager -> userExists ( $share -> getSharedWith ())) {
2024-09-05 12:06:22 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Share recipient is not a valid user' ));
2015-12-15 03:54:12 -05:00
}
2020-06-24 10:49:16 -04:00
} elseif ( $share -> getShareType () === IShare :: TYPE_GROUP ) {
2015-12-15 03:54:12 -05:00
// We expect a valid group as sharedWith for group shares
2016-02-02 15:02:09 -05:00
if ( ! $this -> groupManager -> groupExists ( $share -> getSharedWith ())) {
2024-09-05 12:06:22 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Share recipient is not a valid group' ));
2015-12-15 03:54:12 -05:00
}
2020-06-24 10:49:16 -04:00
} elseif ( $share -> getShareType () === IShare :: TYPE_LINK ) {
2020-10-20 07:58:01 -04:00
// No check for TYPE_EMAIL here as we have a recipient for them
2015-12-15 03:54:12 -05:00
if ( $share -> getSharedWith () !== null ) {
2024-09-05 12:06:22 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Share recipient should be empty' ));
2015-12-15 03:54:12 -05:00
}
2020-10-20 07:58:01 -04:00
} elseif ( $share -> getShareType () === IShare :: TYPE_EMAIL ) {
2016-02-04 04:00:55 -05:00
if ( $share -> getSharedWith () === null ) {
2024-09-05 12:06:22 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Share recipient should not be empty' ));
2016-02-04 04:00:55 -05:00
}
2020-10-20 07:58:01 -04:00
} elseif ( $share -> getShareType () === IShare :: TYPE_REMOTE ) {
2018-06-13 08:19:59 -04:00
if ( $share -> getSharedWith () === null ) {
2024-09-05 12:06:22 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Share recipient should not be empty' ));
2018-06-13 08:19:59 -04:00
}
2020-10-20 07:58:01 -04:00
} elseif ( $share -> getShareType () === IShare :: TYPE_REMOTE_GROUP ) {
2016-07-29 09:38:31 -04:00
if ( $share -> getSharedWith () === null ) {
2024-09-05 12:06:22 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Share recipient should not be empty' ));
2016-07-29 09:38:31 -04:00
}
2020-06-24 10:49:16 -04:00
} elseif ( $share -> getShareType () === IShare :: TYPE_CIRCLE ) {
2017-07-11 07:21:24 -04:00
$circle = \OCA\Circles\Api\v1\Circles :: detailsCircle ( $share -> getSharedWith ());
2017-03-17 15:48:33 -04:00
if ( $circle === null ) {
2024-09-05 12:06:22 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Share recipient is not a valid circle' ));
2017-03-17 15:48:33 -04:00
}
2020-06-24 10:49:16 -04:00
} elseif ( $share -> getShareType () === IShare :: TYPE_ROOM ) {
2020-12-03 10:03:09 -05:00
} elseif ( $share -> getShareType () === IShare :: TYPE_DECK ) {
2023-02-20 04:50:31 -05:00
} elseif ( $share -> getShareType () === IShare :: TYPE_SCIENCEMESH ) {
2015-12-15 03:54:12 -05:00
} else {
2021-04-15 09:42:43 -04:00
// We cannot handle other types yet
2024-09-03 15:49:04 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Unknown share type' ));
2015-12-15 03:54:12 -05:00
}
2016-01-22 08:52:20 -05:00
// Verify the initiator of the share is set
2015-12-15 03:54:12 -05:00
if ( $share -> getSharedBy () === null ) {
2024-09-05 12:06:22 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Share initiator must be set' ));
2015-12-15 03:54:12 -05:00
}
// Cannot share with yourself
2020-06-24 10:49:16 -04:00
if ( $share -> getShareType () === IShare :: TYPE_USER &&
2016-02-02 15:02:09 -05:00
$share -> getSharedWith () === $share -> getSharedBy ()) {
2023-08-30 09:22:45 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Cannot share with yourself' ));
2015-12-15 03:54:12 -05:00
}
// The path should be set
2016-01-27 14:51:26 -05:00
if ( $share -> getNode () === null ) {
2024-09-05 12:06:22 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Shared path must be set' ));
2015-12-15 03:54:12 -05:00
}
2016-01-22 08:52:20 -05:00
2015-12-15 03:54:12 -05:00
// And it should be a file or a folder
2016-01-27 14:51:26 -05:00
if ( ! ( $share -> getNode () instanceof \OCP\Files\File ) &&
2021-04-23 14:20:56 -04:00
! ( $share -> getNode () instanceof \OCP\Files\Folder )) {
2024-09-05 12:06:22 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Shared path must be either a file or a folder' ));
2015-12-15 03:54:12 -05:00
}
2021-04-15 09:42:43 -04:00
// And you cannot share your rootfolder
2016-05-11 14:48:27 -04:00
if ( $this -> userManager -> userExists ( $share -> getSharedBy ())) {
2019-07-02 04:22:30 -04:00
$userFolder = $this -> rootFolder -> getUserFolder ( $share -> getSharedBy ());
2016-05-11 14:48:27 -04:00
} else {
2019-07-02 04:22:30 -04:00
$userFolder = $this -> rootFolder -> getUserFolder ( $share -> getShareOwner ());
2016-05-11 14:48:27 -04:00
}
2020-06-03 03:29:19 -04:00
if ( $userFolder -> getId () === $share -> getNode () -> getId ()) {
2023-08-30 09:22:45 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'You cannot share your root folder' ));
2016-02-25 14:22:35 -05:00
}
2015-12-15 03:54:12 -05:00
// Check if we actually have share permissions
2016-01-27 14:51:26 -05:00
if ( ! $share -> getNode () -> isShareable ()) {
2023-08-30 09:22:45 -04:00
throw new GenericShareException ( $this -> l -> t ( 'You are not allowed to share %s' , [ $share -> getNode () -> getName ()]), code : 404 );
2015-12-15 03:54:12 -05:00
}
// Permissions should be set
if ( $share -> getPermissions () === null ) {
2024-09-05 12:06:22 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Valid permissions are required for sharing' ));
2025-01-21 05:59:22 -05:00
}
// Permissions must be valid
if ( $share -> getPermissions () < 0 || $share -> getPermissions () > \OCP\Constants :: PERMISSION_ALL ) {
throw new \InvalidArgumentException ( $this -> l -> t ( 'Valid permissions are required for sharing' ));
}
// Single file shares should never have delete or create permissions
if (( $share -> getNode () instanceof File )
&& (( $share -> getPermissions () & ( \OCP\Constants :: PERMISSION_CREATE | \OCP\Constants :: PERMISSION_DELETE )) !== 0 )) {
throw new \InvalidArgumentException ( $this -> l -> t ( 'File shares cannot have create or delete permissions' ));
2015-12-15 03:54:12 -05:00
}
2019-07-29 09:00:07 -04:00
$permissions = 0 ;
2024-05-02 10:54:34 -04:00
$nodesForUser = $userFolder -> getById ( $share -> getNodeId ());
foreach ( $nodesForUser as $node ) {
if ( $node -> getInternalPath () === '' && ! $node -> getMountPoint () instanceof MoveableMount ) {
// for the root of non-movable mount, the permissions we see if limited by the mount itself,
// so we instead use the "raw" permissions from the storage
$permissions |= $node -> getStorage () -> getPermissions ( '' );
} else {
$permissions |= $node -> getPermissions ();
2019-07-03 10:32:45 -04:00
}
2016-03-30 07:33:16 -04:00
}
2015-12-15 03:54:12 -05:00
// Check that we do not share with more permissions than we have
2016-03-30 07:33:16 -04:00
if ( $share -> getPermissions () & ~ $permissions ) {
2020-02-18 05:02:11 -05:00
$path = $userFolder -> getRelativePath ( $share -> getNode () -> getPath ());
2023-08-30 09:22:45 -04:00
throw new GenericShareException ( $this -> l -> t ( 'Cannot increase permissions of %s' , [ $path ]), code : 404 );
2015-12-15 03:54:12 -05:00
}
// Check that read permissions are always set
2016-06-07 06:28:02 -04:00
// Link shares are allowed to have no read permissions to allow upload to hidden folders
2020-06-24 10:49:16 -04:00
$noReadPermissionRequired = $share -> getShareType () === IShare :: TYPE_LINK
|| $share -> getShareType () === IShare :: TYPE_EMAIL ;
2017-03-29 05:58:04 -04:00
if ( ! $noReadPermissionRequired &&
2016-06-07 06:28:02 -04:00
( $share -> getPermissions () & \OCP\Constants :: PERMISSION_READ ) === 0 ) {
2023-08-30 09:22:45 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Shares need at least read permissions' ));
2015-12-15 03:54:12 -05:00
}
2016-04-15 03:02:01 -04:00
if ( $share -> getNode () instanceof \OCP\Files\File ) {
if ( $share -> getPermissions () & \OCP\Constants :: PERMISSION_DELETE ) {
2023-08-30 09:22:45 -04:00
throw new GenericShareException ( $this -> l -> t ( 'Files cannot be shared with delete permissions' ));
2016-04-15 03:02:01 -04:00
}
if ( $share -> getPermissions () & \OCP\Constants :: PERMISSION_CREATE ) {
2023-08-30 09:22:45 -04:00
throw new GenericShareException ( $this -> l -> t ( 'Files cannot be shared with create permissions' ));
2016-04-15 03:02:01 -04:00
}
}
2015-12-15 03:54:12 -05:00
}
2019-10-28 14:22:05 -04:00
/**
* Validate if the expiration date fits the system settings
*
2020-06-24 10:49:16 -04:00
* @ param IShare $share The share to validate the expiration date of
* @ return IShare The modified share object
2019-10-28 14:22:05 -04:00
* @ throws GenericShareException
* @ throws \InvalidArgumentException
* @ throws \Exception
*/
2020-06-24 10:49:16 -04:00
protected function validateExpirationDateInternal ( IShare $share ) {
2021-03-25 12:32:49 -04:00
$isRemote = $share -> getShareType () === IShare :: TYPE_REMOTE || $share -> getShareType () === IShare :: TYPE_REMOTE_GROUP ;
2019-10-28 14:22:05 -04:00
$expirationDate = $share -> getExpirationDate ();
2021-03-25 12:32:49 -04:00
if ( $isRemote ) {
$defaultExpireDate = $this -> shareApiRemoteDefaultExpireDate ();
$defaultExpireDays = $this -> shareApiRemoteDefaultExpireDays ();
$configProp = 'remote_defaultExpDays' ;
$isEnforced = $this -> shareApiRemoteDefaultExpireDateEnforced ();
} else {
$defaultExpireDate = $this -> shareApiInternalDefaultExpireDate ();
$defaultExpireDays = $this -> shareApiInternalDefaultExpireDays ();
$configProp = 'internal_defaultExpDays' ;
$isEnforced = $this -> shareApiInternalDefaultExpireDateEnforced ();
}
2024-03-20 11:41:26 -04:00
// If $expirationDate is falsy, noExpirationDate is true and expiration not enforced
// Then skip expiration date validation as null is accepted
2024-06-26 06:45:23 -04:00
if ( ! $share -> getNoExpirationDate () || $isEnforced ) {
if ( $expirationDate !== null ) {
2024-03-20 11:41:26 -04:00
$expirationDate -> setTimezone ( $this -> dateTimeZone -> getTimeZone ());
$expirationDate -> setTime ( 0 , 0 , 0 );
$date = new \DateTime ( 'now' , $this -> dateTimeZone -> getTimeZone ());
$date -> setTime ( 0 , 0 , 0 );
if ( $date >= $expirationDate ) {
2023-08-30 09:22:45 -04:00
throw new GenericShareException ( $this -> l -> t ( 'Expiration date is in the past' ), code : 404 );
2024-03-20 11:41:26 -04:00
}
2020-04-21 08:10:33 -04:00
}
2019-10-28 14:22:05 -04:00
2024-03-20 11:41:26 -04: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
2019-10-28 14:22:05 -04:00
}
2024-03-20 11:41:26 -04:00
if ( $fullId === null && $expirationDate === null && $defaultExpireDate ) {
$expirationDate = new \DateTime ( 'now' , $this -> dateTimeZone -> getTimeZone ());
$expirationDate -> setTime ( 0 , 0 , 0 );
$days = ( int ) $this -> config -> getAppValue ( 'core' , $configProp , ( string ) $defaultExpireDays );
if ( $days > $defaultExpireDays ) {
$days = $defaultExpireDays ;
}
$expirationDate -> add ( new \DateInterval ( 'P' . $days . 'D' ));
}
// If we enforce the expiration date check that is does not exceed
if ( $isEnforced ) {
if ( empty ( $expirationDate )) {
2023-08-30 09:22:45 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Expiration date is enforced' ));
2024-03-20 11:41:26 -04:00
}
$date = new \DateTime ( 'now' , $this -> dateTimeZone -> getTimeZone ());
$date -> setTime ( 0 , 0 , 0 );
$date -> add ( new \DateInterval ( 'P' . $defaultExpireDays . 'D' ));
if ( $date < $expirationDate ) {
2023-08-30 09:22:45 -04:00
throw new GenericShareException ( $this -> l -> n ( 'Cannot set expiration date more than %n day in the future' , 'Cannot set expiration date more than %n days in the future' , $defaultExpireDays ), code : 404 );
2024-03-20 11:41:26 -04:00
}
2019-10-28 14:22:05 -04:00
}
}
$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 03:54:12 -05:00
/**
* Validate if the expiration date fits the system settings
*
2020-06-24 10:49:16 -04:00
* @ param IShare $share The share to validate the expiration date of
* @ return IShare The modified share object
2016-02-02 08:18:59 -05:00
* @ throws GenericShareException
* @ throws \InvalidArgumentException
* @ throws \Exception
2015-12-15 03:54:12 -05:00
*/
2021-03-18 12:12:28 -04:00
protected function validateExpirationDateLink ( IShare $share ) {
2016-01-29 16:42:59 -05:00
$expirationDate = $share -> getExpirationDate ();
2024-03-20 11:41:26 -04:00
$isEnforced = $this -> shareApiLinkDefaultExpireDateEnforced ();
// If $expirationDate is falsy, noExpirationDate is true and expiration not enforced
// Then skip expiration date validation as null is accepted
if ( ! ( $share -> getNoExpirationDate () && ! $isEnforced )) {
if ( $expirationDate !== null ) {
$expirationDate -> setTimezone ( $this -> dateTimeZone -> getTimeZone ());
$expirationDate -> setTime ( 0 , 0 , 0 );
2024-06-26 06:45:23 -04:00
2024-03-20 11:41:26 -04:00
$date = new \DateTime ( 'now' , $this -> dateTimeZone -> getTimeZone ());
$date -> setTime ( 0 , 0 , 0 );
if ( $date >= $expirationDate ) {
2023-08-30 09:22:45 -04:00
throw new GenericShareException ( $this -> l -> t ( 'Expiration date is in the past' ), code : 404 );
2024-03-20 11:41:26 -04:00
}
2015-12-15 03:54:12 -05:00
}
2016-02-25 08:21:46 -05:00
2024-03-20 11:41:26 -04: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
2020-04-21 08:10:33 -04:00
}
2024-06-26 06:45:23 -04:00
2024-03-20 11:41:26 -04:00
if ( $fullId === null && $expirationDate === null && $this -> shareApiLinkDefaultExpireDate ()) {
$expirationDate = new \DateTime ( 'now' , $this -> dateTimeZone -> getTimeZone ());
$expirationDate -> setTime ( 0 , 0 , 0 );
2024-06-26 06:45:23 -04:00
2024-03-20 11:41:26 -04:00
$days = ( int ) $this -> config -> getAppValue ( 'core' , 'link_defaultExpDays' , ( string ) $this -> shareApiLinkDefaultExpireDays ());
if ( $days > $this -> shareApiLinkDefaultExpireDays ()) {
$days = $this -> shareApiLinkDefaultExpireDays ();
}
$expirationDate -> add ( new \DateInterval ( 'P' . $days . 'D' ));
2015-12-15 03:54:12 -05:00
}
2024-06-26 06:45:23 -04:00
2024-03-20 11:41:26 -04:00
// If we enforce the expiration date check that is does not exceed
if ( $isEnforced ) {
if ( empty ( $expirationDate )) {
2023-08-30 09:22:45 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Expiration date is enforced' ));
2024-03-20 11:41:26 -04:00
}
2024-06-26 06:45:23 -04:00
2024-03-20 11:41:26 -04:00
$date = new \DateTime ( 'now' , $this -> dateTimeZone -> getTimeZone ());
$date -> setTime ( 0 , 0 , 0 );
$date -> add ( new \DateInterval ( 'P' . $this -> shareApiLinkDefaultExpireDays () . 'D' ));
if ( $date < $expirationDate ) {
2023-08-30 09:22:45 -04:00
throw new GenericShareException (
$this -> l -> n ( 'Cannot set expiration date more than %n day in the future' , 'Cannot set expiration date more than %n days in the future' , $this -> shareApiLinkDefaultExpireDays ()),
code : 404 ,
);
2024-03-20 11:41:26 -04:00
}
2015-12-15 03:54:12 -05:00
}
2024-03-20 11:41:26 -04:00
2015-12-15 03:54:12 -05:00
}
2016-01-29 16:42:59 -05:00
$accepted = true ;
$message = '' ;
\OCP\Util :: emitHook ( '\OC\Share' , 'verifyExpirationDate' , [
'expirationDate' => & $expirationDate ,
'accepted' => & $accepted ,
'message' => & $message ,
2016-02-25 09:39:02 -05:00
'passwordSet' => $share -> getPassword () !== null ,
2016-01-29 16:42:59 -05:00
]);
if ( ! $accepted ) {
throw new \Exception ( $message );
2015-12-15 03:54:12 -05:00
}
2016-01-29 16:42:59 -05:00
$share -> setExpirationDate ( $expirationDate );
2016-02-25 08:21:46 -05:00
return $share ;
2015-12-15 03:54:12 -05:00
}
/**
2016-01-08 08:31:28 -05:00
* Check for pre share requirements for user shares
2015-12-15 03:54:12 -05:00
*
2020-06-24 10:49:16 -04:00
* @ param IShare $share
2021-02-05 06:52:59 -05:00
* @ throws \Exception
2015-12-15 03:54:12 -05:00
*/
2020-06-24 10:49:16 -04:00
protected function userCreateChecks ( IShare $share ) {
2015-12-15 03:54:12 -05:00
// Check if we can share with group members only
if ( $this -> shareWithGroupMembersOnly ()) {
2016-02-02 15:02:09 -05:00
$sharedBy = $this -> userManager -> get ( $share -> getSharedBy ());
$sharedWith = $this -> userManager -> get ( $share -> getSharedWith ());
2015-12-15 03:54:12 -05:00
// Verify we can share with this user
$groups = array_intersect (
2021-04-23 14:20:56 -04:00
$this -> groupManager -> getUserGroupIds ( $sharedBy ),
$this -> groupManager -> getUserGroupIds ( $sharedWith )
2015-12-15 03:54:12 -05:00
);
2023-12-27 06:56:23 -05:00
// optional excluded groups
$excludedGroups = $this -> shareWithGroupMembersOnlyExcludeGroupsList ();
$groups = array_diff ( $groups , $excludedGroups );
2015-12-15 03:54:12 -05:00
if ( empty ( $groups )) {
2023-08-30 09:22:45 -04:00
throw new \Exception ( $this -> l -> t ( 'Sharing is only allowed with group members' ));
2015-12-15 03:54:12 -05:00
}
}
/*
* TODO : Could be costly , fix
*
* Also this is not what we want in the future .. then we want to squash identical shares .
*/
2020-06-24 10:49:16 -04:00
$provider = $this -> factory -> getProviderForType ( IShare :: TYPE_USER );
2016-01-27 14:51:26 -05:00
$existingShares = $provider -> getSharesByPath ( $share -> getNode ());
2015-12-15 03:54:12 -05:00
foreach ( $existingShares as $existingShare ) {
2016-01-22 08:52:20 -05:00
// Ignore if it is the same share
2016-02-08 15:08:03 -05:00
try {
if ( $existingShare -> getFullId () === $share -> getFullId ()) {
continue ;
}
} catch ( \UnexpectedValueException $e ) {
//Shares are not identical
2016-01-22 08:52:20 -05:00
}
2020-10-27 09:29:28 -04:00
// Identical share already exists
2019-03-20 07:50:48 -04:00
if ( $existingShare -> getSharedWith () === $share -> getSharedWith () && $existingShare -> getShareType () === $share -> getShareType ()) {
2023-08-30 09:22:45 -04:00
throw new AlreadySharedException ( $this -> l -> t ( 'Sharing %s failed, because this item is already shared with the account %s' , [ $share -> getNode () -> getName (), $share -> getSharedWithDisplayName ()]), $existingShare );
2015-12-15 03:54:12 -05:00
}
// The share is already shared with this user via a group share
2020-06-24 10:49:16 -04:00
if ( $existingShare -> getShareType () === IShare :: TYPE_GROUP ) {
2016-02-02 15:02:09 -05:00
$group = $this -> groupManager -> get ( $existingShare -> getSharedWith ());
2017-01-19 09:02:46 -05:00
if ( ! is_null ( $group )) {
$user = $this -> userManager -> get ( $share -> getSharedWith ());
2016-02-02 15:02:09 -05:00
2017-01-19 09:02:46 -05:00
if ( $group -> inGroup ( $user ) && $existingShare -> getShareOwner () !== $share -> getShareOwner ()) {
2023-08-30 09:22:45 -04:00
throw new AlreadySharedException ( $this -> l -> t ( 'Sharing %s failed, because this item is already shared with the account %s' , [ $share -> getNode () -> getName (), $share -> getSharedWithDisplayName ()]), $existingShare );
2017-01-19 09:02:46 -05:00
}
2016-02-02 15:02:09 -05:00
}
2015-12-15 03:54:12 -05:00
}
}
}
/**
* Check for pre share requirements for group shares
*
2020-06-24 10:49:16 -04:00
* @ param IShare $share
2015-12-15 03:54:12 -05:00
* @ throws \Exception
*/
2020-06-24 10:49:16 -04:00
protected function groupCreateChecks ( IShare $share ) {
2016-03-18 11:36:27 -04:00
// Verify group shares are allowed
if ( ! $this -> allowGroupSharing ()) {
2023-08-30 09:22:45 -04:00
throw new \Exception ( $this -> l -> t ( 'Group sharing is now allowed' ));
2016-03-18 11:36:27 -04:00
}
2015-12-15 03:54:12 -05:00
// Verify if the user can share with this group
if ( $this -> shareWithGroupMembersOnly ()) {
2016-02-02 15:02:09 -05:00
$sharedBy = $this -> userManager -> get ( $share -> getSharedBy ());
$sharedWith = $this -> groupManager -> get ( $share -> getSharedWith ());
2023-12-27 06:56:23 -05:00
// optional excluded groups
$excludedGroups = $this -> shareWithGroupMembersOnlyExcludeGroupsList ();
if ( is_null ( $sharedWith ) || in_array ( $share -> getSharedWith (), $excludedGroups ) || ! $sharedWith -> inGroup ( $sharedBy )) {
2023-08-30 09:22:45 -04:00
throw new \Exception ( $this -> l -> t ( 'Sharing is only allowed within your own groups' ));
2015-12-15 03:54:12 -05:00
}
}
/*
* TODO : Could be costly , fix
*
* Also this is not what we want in the future .. then we want to squash identical shares .
*/
2020-06-24 10:49:16 -04:00
$provider = $this -> factory -> getProviderForType ( IShare :: TYPE_GROUP );
2016-01-27 14:51:26 -05:00
$existingShares = $provider -> getSharesByPath ( $share -> getNode ());
2015-12-15 03:54:12 -05:00
foreach ( $existingShares as $existingShare ) {
2016-02-08 09:28:36 -05:00
try {
if ( $existingShare -> getFullId () === $share -> getFullId ()) {
continue ;
}
} catch ( \UnexpectedValueException $e ) {
//It is a new share so just continue
2016-01-22 08:52:20 -05:00
}
2019-03-20 07:51:26 -04:00
if ( $existingShare -> getSharedWith () === $share -> getSharedWith () && $existingShare -> getShareType () === $share -> getShareType ()) {
2023-08-30 09:22:45 -04:00
throw new AlreadySharedException ( $this -> l -> t ( 'Path is already shared with this group' ), $existingShare );
2015-12-15 03:54:12 -05:00
}
}
}
/**
* Check for pre share requirements for link shares
*
2020-06-24 10:49:16 -04:00
* @ param IShare $share
2015-12-15 03:54:12 -05:00
* @ throws \Exception
*/
2020-06-24 10:49:16 -04:00
protected function linkCreateChecks ( IShare $share ) {
2015-12-15 03:54:12 -05:00
// Are link shares allowed?
if ( ! $this -> shareApiAllowLinks ()) {
2023-08-30 09:22:45 -04:00
throw new \Exception ( $this -> l -> t ( 'Link sharing is not allowed' ));
2015-12-15 03:54:12 -05:00
}
// Check if public upload is allowed
2022-06-21 08:25:36 -04:00
if ( $share -> getNodeType () === 'folder' && ! $this -> shareApiLinkAllowPublicUpload () &&
2016-06-23 09:43:21 -04:00
( $share -> getPermissions () & ( \OCP\Constants :: PERMISSION_CREATE | \OCP\Constants :: PERMISSION_UPDATE | \OCP\Constants :: PERMISSION_DELETE ))) {
2023-08-30 09:22:45 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Public upload is not allowed' ));
2015-12-15 03:54:12 -05:00
}
}
2016-02-11 05:55:09 -05: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
*
2020-06-24 10:49:16 -04:00
* @ param IShare $share
2016-02-11 05:55:09 -05:00
*/
2020-06-24 10:49:16 -04:00
protected function setLinkParent ( IShare $share ) {
2016-02-11 05:55:09 -05:00
// No sense in checking if the method is not there.
if ( method_exists ( $share , 'setParent' )) {
$storage = $share -> getNode () -> getStorage ();
2024-08-05 08:51:14 -04:00
if ( $storage -> instanceOfStorage ( SharedStorage :: class )) {
2016-12-07 01:11:32 -05:00
/** @var \OCA\Files_Sharing\SharedStorage $storage */
2016-02-11 05:55:09 -05:00
$share -> setParent ( $storage -> getShareId ());
}
2018-01-26 17:46:40 -05:00
}
2016-02-11 05:55:09 -05:00
}
2015-12-15 03:54:12 -05: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 ());
foreach ( $mounts as $mount ) {
if ( $mount -> getStorage () -> instanceOfStorage ( '\OCA\Files_Sharing\ISharedStorage' )) {
2025-04-09 10:12:18 -04:00
// Using a flat sharing model ensures the file owner can always see who has access.
// Allowing parent folder sharing would require tracking inherited access, which adds complexity
// and hurts performance/scalability.
// So we forbid sharing a parent folder of a share you received.
throw new \InvalidArgumentException ( $this -> l -> t ( 'You cannot share a folder that contains other shares' ));
2015-12-15 03:54:12 -05:00
}
}
}
}
/**
* Check if the user that is sharing can actually share
*
2020-06-24 10:49:16 -04:00
* @ param IShare $share
2016-02-16 04:22:49 -05:00
* @ throws \Exception
2015-12-15 03:54:12 -05:00
*/
2020-06-24 10:49:16 -04:00
protected function canShare ( IShare $share ) {
2015-12-15 03:54:12 -05:00
if ( ! $this -> shareApiEnabled ()) {
2023-08-30 09:22:45 -04:00
throw new \Exception ( $this -> l -> t ( 'Sharing is disabled' ));
2015-12-15 03:54:12 -05:00
}
2016-01-27 14:51:26 -05:00
if ( $this -> sharingDisabledForUser ( $share -> getSharedBy ())) {
2023-08-30 09:22:45 -04:00
throw new \Exception ( $this -> l -> t ( 'Sharing is disabled for you' ));
2015-12-15 03:54:12 -05:00
}
}
2015-10-30 08:09:07 -04:00
/**
* Share a path
2015-11-24 03:58:37 -05:00
*
2020-06-24 10:49:16 -04:00
* @ param IShare $share
2020-06-26 04:57:14 -04:00
* @ return IShare The share object
2015-12-15 03:54:12 -05:00
* @ throws \Exception
*
* TODO : handle link share permissions or check them
2015-10-30 08:09:07 -04:00
*/
2020-06-24 10:49:16 -04:00
public function createShare ( IShare $share ) {
2016-02-16 04:22:49 -05:00
$this -> canShare ( $share );
2015-12-15 03:54:12 -05:00
$this -> generalCreateChecks ( $share );
2016-04-15 03:02:01 -04: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 ();
while ( $parent -> getStorage () -> instanceOfStorage ( 'OCA\Files_Sharing\External\Storage' )) {
$parent = $parent -> getParent ();
}
$share -> setShareOwner ( $parent -> getOwner () -> getUID ());
} else {
2020-03-13 12:17:43 -04:00
if ( $share -> getNode () -> getOwner ()) {
$share -> setShareOwner ( $share -> getNode () -> getOwner () -> getUID ());
} else {
$share -> setShareOwner ( $share -> getSharedBy ());
}
2016-04-15 03:02:01 -04:00
}
2021-02-08 10:02:08 -05:00
try {
2020-12-29 18:48:01 -05:00
// Verify share type
2021-02-08 10:02:08 -05:00
if ( $share -> getShareType () === IShare :: TYPE_USER ) {
$this -> userCreateChecks ( $share );
2020-12-29 18:48:01 -05:00
// Verify the expiration date
2021-02-08 10:02:08 -05:00
$share = $this -> validateExpirationDateInternal ( $share );
} elseif ( $share -> getShareType () === IShare :: TYPE_GROUP ) {
$this -> groupCreateChecks ( $share );
2020-12-29 18:48:01 -05:00
// Verify the expiration date
2021-02-08 10:02:08 -05:00
$share = $this -> validateExpirationDateInternal ( $share );
2021-01-25 09:26:16 -05:00
} elseif ( $share -> getShareType () === IShare :: TYPE_REMOTE || $share -> getShareType () === IShare :: TYPE_REMOTE_GROUP ) {
2024-07-05 03:47:40 -04:00
// Verify the expiration date
2021-01-25 09:26:16 -05:00
$share = $this -> validateExpirationDateInternal ( $share );
2020-10-20 07:58:01 -04:00
} elseif ( $share -> getShareType () === IShare :: TYPE_LINK
|| $share -> getShareType () === IShare :: TYPE_EMAIL ) {
2021-02-08 10:02:08 -05:00
$this -> linkCreateChecks ( $share );
$this -> setLinkParent ( $share );
2024-12-16 18:53:12 -05:00
$token = $this -> generateToken ();
2024-08-15 14:46:46 -04:00
// Set the unique token
$share -> setToken ( $token );
2021-02-08 10:02:08 -05:00
2020-10-20 07:58:01 -04:00
// Verify the expiration date
2021-03-18 06:41:10 -04:00
$share = $this -> validateExpirationDateLink ( $share );
2021-02-08 10:02:08 -05:00
2020-10-20 07:58:01 -04:00
// Verify the password
2021-02-08 10:02:08 -05:00
$this -> verifyPassword ( $share -> getPassword ());
// If a password is set. Hash it!
2021-03-18 06:41:10 -04:00
if ( $share -> getShareType () === IShare :: TYPE_LINK
&& $share -> getPassword () !== null ) {
2021-02-08 10:02:08 -05:00
$share -> setPassword ( $this -> hasher -> hash ( $share -> getPassword ()));
}
2015-12-15 03:54:12 -05:00
}
2021-02-08 10:02:08 -05:00
// Cannot share with the owner
if ( $share -> getShareType () === IShare :: TYPE_USER &&
$share -> getSharedWith () === $share -> getShareOwner ()) {
2023-08-30 09:22:45 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Cannot share with the share owner' ));
2021-02-08 10:02:08 -05:00
}
2016-01-22 08:52:20 -05:00
2021-02-08 10:02:08 -05:00
// Generate the target
2021-07-25 11:57:11 -04:00
$defaultShareFolder = $this -> config -> getSystemValue ( 'share_folder' , '/' );
$allowCustomShareFolder = $this -> config -> getSystemValueBool ( 'sharing.allow_custom_share_folder' , true );
if ( $allowCustomShareFolder ) {
$shareFolder = $this -> config -> getUserValue ( $share -> getSharedWith (), Application :: APP_ID , 'share_folder' , $defaultShareFolder );
} else {
$shareFolder = $defaultShareFolder ;
}
$target = $shareFolder . '/' . $share -> getNode () -> getName ();
2021-02-08 10:02:08 -05:00
$target = \OC\Files\Filesystem :: normalizePath ( $target );
$share -> setTarget ( $target );
2015-12-15 03:54:12 -05:00
2021-02-08 10:02:08 -05:00
// Pre share event
2023-07-27 15:45:31 -04:00
$event = new Share\Events\BeforeShareCreatedEvent ( $share );
$this -> dispatcher -> dispatchTyped ( $event );
if ( $event -> isPropagationStopped () && $event -> getError ()) {
throw new \Exception ( $event -> getError ());
2021-02-08 10:02:08 -05:00
}
2015-12-15 03:54:12 -05:00
2021-02-08 10:02:08 -05:00
$oldShare = $share ;
$provider = $this -> factory -> getProviderForType ( $share -> getShareType ());
$share = $provider -> create ( $share );
2020-12-29 18:48:01 -05:00
// Reuse the node we already have
2021-02-08 10:02:08 -05:00
$share -> setNode ( $oldShare -> getNode ());
2015-12-15 03:54:12 -05:00
2021-02-08 10:02:08 -05:00
// Reset the target if it is null for the new share
if ( $share -> getTarget () === '' ) {
$share -> setTarget ( $target );
}
} catch ( AlreadySharedException $e ) {
2023-08-30 09:22:45 -04:00
// If a share for the same target already exists, dont create a new one,
// but do trigger the hooks and notifications again
2023-01-25 11:06:17 -05:00
$oldShare = $share ;
// Reuse the node we already have
2021-02-08 10:02:08 -05:00
$share = $e -> getExistingShare ();
2023-01-25 11:06:17 -05:00
$share -> setNode ( $oldShare -> getNode ());
2020-03-16 08:38:55 -04:00
}
2017-08-03 09:14:29 -04:00
// Post share event
2023-07-27 15:45:31 -04:00
$this -> dispatcher -> dispatchTyped ( new ShareCreatedEvent ( $share ));
2015-12-15 03:54:12 -05:00
2024-07-05 03:47:40 -04:00
// Send email if needed
if ( $this -> config -> getSystemValueBool ( 'sharing.enable_share_mail' , true )) {
if ( $share -> getMailSend ()) {
$provider = $this -> factory -> getProviderForType ( $share -> getShareType ());
if ( $provider instanceof IShareProviderWithNotification ) {
$provider -> sendMailNotification ( $share );
2017-07-26 17:53:07 -04:00
} else {
2024-07-05 03:47:40 -04:00
$this -> logger -> debug ( 'Share notification not sent because the provider does not support it.' , [ 'app' => 'share' ]);
2017-07-26 17:53:07 -04:00
}
} else {
2019-01-30 09:32:19 -05:00
$this -> logger -> debug ( 'Share notification not sent because mailsend is false.' , [ 'app' => 'share' ]);
2017-07-26 17:53:07 -04:00
}
} else {
2024-07-05 03:47:40 -04:00
$this -> logger -> debug ( 'Share notification not sent because sharing notification emails is disabled.' , [ 'app' => 'share' ]);
2017-07-26 17:53:07 -04:00
}
2024-07-05 03:47:40 -04:00
return $share ;
2017-07-26 17:53:07 -04:00
}
2015-10-30 08:09:07 -04:00
/**
* Update a share
*
2020-06-24 10:49:16 -04:00
* @ param IShare $share
* @ return IShare The share object
2016-02-02 15:02:09 -05:00
* @ throws \InvalidArgumentException
2024-12-10 09:27:38 -05:00
* @ throws HintException
2015-10-30 08:09:07 -04:00
*/
2024-11-14 01:41:41 -05:00
public function updateShare ( IShare $share , bool $onlyValid = true ) {
2016-01-25 07:09:25 -05:00
$expirationDateUpdated = false ;
2016-02-16 04:22:49 -05:00
$this -> canShare ( $share );
2016-01-22 08:52:20 -05:00
2016-02-08 15:08:03 -05:00
try {
2024-11-14 01:41:41 -05:00
$originalShare = $this -> getShareById ( $share -> getFullId (), onlyValid : $onlyValid );
2016-02-08 15:08:03 -05:00
} catch ( \UnexpectedValueException $e ) {
2024-09-06 13:21:07 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Share does not have a full ID' ));
2016-02-08 15:08:03 -05:00
}
2016-01-22 08:52:20 -05:00
2021-04-15 09:42:43 -04:00
// We cannot change the share type!
2016-01-22 08:52:20 -05:00
if ( $share -> getShareType () !== $originalShare -> getShareType ()) {
2023-08-30 09:22:45 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Cannot change share type' ));
2016-01-22 08:52:20 -05:00
}
// We can only change the recipient on user shares
if ( $share -> getSharedWith () !== $originalShare -> getSharedWith () &&
2020-06-24 10:49:16 -04:00
$share -> getShareType () !== IShare :: TYPE_USER ) {
2023-08-30 09:22:45 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Can only update recipient on user shares' ));
2016-01-22 08:52:20 -05:00
}
// Cannot share with the owner
2020-06-24 10:49:16 -04:00
if ( $share -> getShareType () === IShare :: TYPE_USER &&
2016-02-02 15:02:09 -05:00
$share -> getSharedWith () === $share -> getShareOwner ()) {
2023-08-30 09:22:45 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Cannot share with the share owner' ));
2016-01-22 08:52:20 -05:00
}
2024-03-19 21:30:00 -04:00
$this -> generalCreateChecks ( $share , true );
2016-01-22 08:52:20 -05:00
2020-06-24 10:49:16 -04:00
if ( $share -> getShareType () === IShare :: TYPE_USER ) {
2016-01-22 08:52:20 -05:00
$this -> userCreateChecks ( $share );
2019-10-28 14:22:05 -04:00
if ( $share -> getExpirationDate () != $originalShare -> getExpirationDate ()) {
2023-08-30 09:22:45 -04:00
// Verify the expiration date
2021-03-18 12:12:28 -04:00
$this -> validateExpirationDateInternal ( $share );
2019-10-28 14:22:05 -04:00
$expirationDateUpdated = true ;
}
2020-06-24 10:49:16 -04:00
} elseif ( $share -> getShareType () === IShare :: TYPE_GROUP ) {
2016-01-22 08:52:20 -05:00
$this -> groupCreateChecks ( $share );
2019-10-28 14:22:05 -04:00
if ( $share -> getExpirationDate () != $originalShare -> getExpirationDate ()) {
2023-08-30 09:22:45 -04:00
// Verify the expiration date
2021-03-18 12:12:28 -04:00
$this -> validateExpirationDateInternal ( $share );
2019-10-28 14:22:05 -04:00
$expirationDateUpdated = true ;
}
2020-10-20 07:58:01 -04:00
} elseif ( $share -> getShareType () === IShare :: TYPE_LINK
|| $share -> getShareType () === IShare :: TYPE_EMAIL ) {
2016-01-22 08:52:20 -05:00
$this -> linkCreateChecks ( $share );
2021-03-18 06:41:10 -04:00
// The new password is not set again if it is the same as the old
// one, unless when switching from sending by Talk to sending by
// mail.
2020-05-28 14:29:28 -04:00
$plainTextPassword = $share -> getPassword ();
2021-03-18 06:41:10 -04:00
$updatedPassword = $this -> updateSharePasswordIfNeeded ( $share , $originalShare );
2020-05-28 14:29:28 -04:00
2021-03-18 06:41:10 -04:00
/**
* Cannot enable the getSendPasswordByTalk if there is no password set
*/
if ( empty ( $plainTextPassword ) && $share -> getSendPasswordByTalk ()) {
2023-08-30 09:22:45 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Cannot enable sending the password by Talk with an empty password' ));
2021-03-18 06:41:10 -04:00
}
2021-01-25 09:26:16 -05:00
2021-03-18 06:41:10 -04:00
/**
* If we ' re in a mail share , we need to force a password change
* as either the user is not aware of the password or is already ( received by mail )
* Thus the SendPasswordByTalk feature would not make sense
*/
if ( ! $updatedPassword && $share -> getShareType () === IShare :: TYPE_EMAIL ) {
2020-12-29 18:48:01 -05:00
if ( ! $originalShare -> getSendPasswordByTalk () && $share -> getSendPasswordByTalk ()) {
2023-08-30 09:22:45 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Cannot enable sending the password by Talk without setting a new password' ));
2020-12-29 18:48:01 -05:00
}
if ( $originalShare -> getSendPasswordByTalk () && ! $share -> getSendPasswordByTalk ()) {
2023-08-30 09:22:45 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Cannot disable sending the password by Talk without setting a new password' ));
2020-12-29 18:48:01 -05:00
}
2017-03-28 08:39:38 -04:00
}
2020-10-20 07:58:01 -04:00
if ( $share -> getExpirationDate () != $originalShare -> getExpirationDate ()) {
// Verify the expiration date
2021-03-18 06:41:10 -04:00
$this -> validateExpirationDateLink ( $share );
2020-10-20 07:58:01 -04:00
$expirationDateUpdated = true ;
}
2021-01-25 09:26:16 -05:00
} elseif ( $share -> getShareType () === IShare :: TYPE_REMOTE || $share -> getShareType () === IShare :: TYPE_REMOTE_GROUP ) {
if ( $share -> getExpirationDate () != $originalShare -> getExpirationDate ()) {
2023-08-30 09:22:45 -04:00
// Verify the expiration date
2021-01-25 09:26:16 -05:00
$this -> validateExpirationDateInternal ( $share );
$expirationDateUpdated = true ;
}
2017-03-28 08:39:38 -04:00
}
2016-01-27 14:51:26 -05:00
$this -> pathCreateChecks ( $share -> getNode ());
2016-01-22 08:52:20 -05:00
// Now update the share!
$provider = $this -> factory -> getProviderForType ( $share -> getShareType ());
2020-06-24 10:49:16 -04:00
if ( $share -> getShareType () === IShare :: TYPE_EMAIL ) {
2017-03-28 08:39:38 -04:00
$share = $provider -> update ( $share , $plainTextPassword );
} else {
$share = $provider -> update ( $share );
}
2016-01-25 07:09:25 -05:00
if ( $expirationDateUpdated === true ) {
2018-01-25 17:16:13 -05:00
\OC_Hook :: emit ( Share :: class , 'post_set_expiration_date' , [
2016-01-27 14:51:26 -05:00
'itemType' => $share -> getNode () instanceof \OCP\Files\File ? 'file' : 'folder' ,
'itemSource' => $share -> getNode () -> getId (),
2016-01-25 07:09:25 -05:00
'date' => $share -> getExpirationDate (),
2016-02-02 15:02:09 -05:00
'uidOwner' => $share -> getSharedBy (),
2016-01-25 07:09:25 -05:00
]);
}
2016-02-09 04:38:12 -05:00
if ( $share -> getPassword () !== $originalShare -> getPassword ()) {
2018-01-25 17:16:13 -05:00
\OC_Hook :: emit ( Share :: class , 'post_update_password' , [
2016-02-09 04:38:12 -05: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 03:53:47 -05:00
if ( $share -> getPermissions () !== $originalShare -> getPermissions ()) {
2016-05-11 14:48:27 -04:00
if ( $this -> userManager -> userExists ( $share -> getShareOwner ())) {
$userFolder = $this -> rootFolder -> getUserFolder ( $share -> getShareOwner ());
} else {
$userFolder = $this -> rootFolder -> getUserFolder ( $share -> getSharedBy ());
}
2020-03-26 04:30:18 -04:00
\OC_Hook :: emit ( Share :: class , 'post_update_permissions' , [
2016-02-04 03:53:47 -05: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 (),
2022-05-25 03:55:22 -04:00
'attributes' => $share -> getAttributes () !== null ? $share -> getAttributes () -> toArray () : null ,
2016-02-04 07:25:24 -05:00
'path' => $userFolder -> getRelativePath ( $share -> getNode () -> getPath ()),
2020-03-26 04:30:18 -04:00
]);
2016-02-04 03:53:47 -05:00
}
2016-01-25 07:09:25 -05:00
return $share ;
2015-10-30 08:09:07 -04:00
}
2019-09-04 10:50:52 -04:00
/**
* Accept a share .
*
* @ param IShare $share
* @ param string $recipientId
* @ return IShare The share object
2024-06-25 06:40:27 -04:00
* @ throws \InvalidArgumentException Thrown if the provider does not implement `IShareProviderSupportsAccept`
2019-09-04 10:50:52 -04:00
* @ since 9.0 . 0
*/
public function acceptShare ( IShare $share , string $recipientId ) : IShare {
2021-04-23 14:20:56 -04:00
[ $providerId ,] = $this -> splitFullId ( $share -> getFullId ());
2019-09-04 10:50:52 -04:00
$provider = $this -> factory -> getProvider ( $providerId );
2024-06-25 06:40:27 -04:00
if ( ! ( $provider instanceof IShareProviderSupportsAccept )) {
2023-08-30 09:22:45 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Share provider does not support accepting' ));
2019-09-04 10:50:52 -04:00
}
2024-06-25 06:40:27 -04:00
/** @var IShareProvider&IShareProviderSupportsAccept $provider */
2019-09-04 10:50:52 -04:00
$provider -> acceptShare ( $share , $recipientId );
2023-07-27 16:36:13 -04:00
$event = new ShareAcceptedEvent ( $share );
$this -> dispatcher -> dispatchTyped ( $event );
2019-09-04 10:50:52 -04:00
return $share ;
}
2017-04-23 13:47:28 -04:00
/**
* Updates the password of the given share if it is not the same as the
* password of the original share .
*
2020-06-24 10:49:16 -04:00
* @ param IShare $share the share to update its password .
* @ param IShare $originalShare the original share to compare its
2017-04-23 13:47:28 -04:00
* password with .
* @ return boolean whether the password was updated or not .
*/
2020-06-24 10:49:16 -04:00
private function updateSharePasswordIfNeeded ( IShare $share , IShare $originalShare ) {
2020-05-28 14:37:18 -04:00
$passwordsAreDifferent = ( $share -> getPassword () !== $originalShare -> getPassword ()) &&
2021-04-23 14:20:56 -04:00
(( $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 ())));
2020-05-28 14:37:18 -04:00
2017-04-23 13:47:28 -04:00
// Password updated.
2020-05-28 14:37:18 -04:00
if ( $passwordsAreDifferent ) {
2023-08-30 09:22:45 -04:00
// Verify the password
2017-04-23 13:47:28 -04:00
$this -> verifyPassword ( $share -> getPassword ());
// If a password is set. Hash it!
2021-03-18 06:41:10 -04:00
if ( ! empty ( $share -> getPassword ())) {
2017-04-23 13:47:28 -04:00
$share -> setPassword ( $this -> hasher -> hash ( $share -> getPassword ()));
2022-04-13 09:46:30 -04:00
if ( $share -> getShareType () === IShare :: TYPE_EMAIL ) {
// Shares shared by email have temporary passwords
$this -> setSharePasswordExpirationTime ( $share );
}
2017-04-23 13:47:28 -04:00
2021-03-18 06:41:10 -04:00
return true ;
} else {
// Empty string and null are seen as NOT password protected
$share -> setPassword ( null );
2022-04-13 09:46:30 -04:00
if ( $share -> getShareType () === IShare :: TYPE_EMAIL ) {
$share -> setPasswordExpirationTime ( null );
}
2017-04-23 13:47:28 -04:00
return true ;
}
2020-05-28 14:37:18 -04: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 13:47:28 -04:00
}
return false ;
}
2022-04-13 09:46:30 -04:00
/**
* Set the share ' s password expiration time
*/
private function setSharePasswordExpirationTime ( IShare $share ) : void {
2023-04-05 06:50:08 -04:00
if ( ! $this -> config -> getSystemValueBool ( 'sharing.enable_mail_link_password_expiration' , false )) {
2022-04-13 09:46:30 -04:00
// Sets password expiration date to NULL
$share -> setPasswordExpirationTime ();
return ;
}
// Sets password expiration date
$expirationTime = null ;
2022-04-14 08:06:25 -04:00
$now = new \DateTime ();
$expirationInterval = $this -> config -> getSystemValue ( 'sharing.mail_link_password_expiration_interval' , 3600 );
$expirationTime = $now -> add ( new \DateInterval ( 'PT' . $expirationInterval . 'S' ));
$share -> setPasswordExpirationTime ( $expirationTime );
2022-04-13 09:46:30 -04:00
}
2015-11-10 08:14:49 -05:00
/**
* Delete all the children of this share
2016-01-29 05:41:28 -05:00
* FIXME : remove once https :// github . com / owncloud / core / pull / 21660 is in
2015-11-10 08:14:49 -05:00
*
2020-06-24 10:49:16 -04:00
* @ param IShare $share
* @ return IShare [] List of deleted shares
2015-11-10 08:14:49 -05:00
*/
2020-06-24 10:49:16 -04:00
protected function deleteChildren ( IShare $share ) {
2015-11-10 08:14:49 -05:00
$deletedShares = [];
2016-01-13 08:21:55 -05:00
$provider = $this -> factory -> getProviderForType ( $share -> getShareType ());
2016-01-08 08:31:28 -05:00
2016-01-13 07:02:23 -05:00
foreach ( $provider -> getChildren ( $share ) as $child ) {
2023-07-27 16:25:26 -04:00
$this -> dispatcher -> dispatchTyped ( new BeforeShareDeletedEvent ( $child ));
2016-01-13 07:02:23 -05:00
$deletedChildren = $this -> deleteChildren ( $child );
$deletedShares = array_merge ( $deletedShares , $deletedChildren );
2016-01-08 08:31:28 -05:00
2016-01-13 07:02:23 -05:00
$provider -> delete ( $child );
2023-07-27 16:25:26 -04:00
$this -> dispatcher -> dispatchTyped ( new ShareDeletedEvent ( $child ));
2016-01-13 07:02:23 -05:00
$deletedShares [] = $child ;
2015-11-10 08:14:49 -05:00
}
return $deletedShares ;
}
2024-09-15 11:16:55 -04:00
/** Promote re-shares into direct shares so that target user keeps access */
2024-09-13 04:00:53 -04:00
protected function promoteReshares ( IShare $share ) : void {
2024-08-07 05:13:35 -04:00
try {
$node = $share -> getNode ();
} catch ( NotFoundException ) {
2024-08-22 11:03:07 -04:00
/* Skip if node not found */
2024-08-07 05:13:35 -04:00
return ;
}
$userIds = [];
2024-08-22 11:03:07 -04:00
if ( $share -> getShareType () === IShare :: TYPE_USER ) {
2024-08-07 05:13:35 -04:00
$userIds [] = $share -> getSharedWith ();
2024-08-22 11:03:07 -04:00
} elseif ( $share -> getShareType () === IShare :: TYPE_GROUP ) {
2024-08-07 05:13:35 -04:00
$group = $this -> groupManager -> get ( $share -> getSharedWith ());
2024-08-22 11:03:07 -04:00
$users = $group ? -> getUsers () ? ? [];
2024-08-07 05:13:35 -04:00
foreach ( $users as $user ) {
2024-08-22 11:03:07 -04:00
/* Skip share owner */
2024-09-13 04:00:53 -04:00
if ( $user -> getUID () === $share -> getShareOwner () || $user -> getUID () === $share -> getSharedBy ()) {
2024-08-07 05:13:35 -04:00
continue ;
}
$userIds [] = $user -> getUID ();
}
2024-08-22 11:03:07 -04:00
} else {
/* We only support user and group shares */
return ;
2024-08-07 05:13:35 -04:00
}
$reshareRecords = [];
$shareTypes = [
IShare :: TYPE_GROUP ,
IShare :: TYPE_USER ,
IShare :: TYPE_LINK ,
IShare :: TYPE_REMOTE ,
2024-08-22 11:03:07 -04:00
IShare :: TYPE_EMAIL ,
2024-08-07 05:13:35 -04:00
];
foreach ( $userIds as $userId ) {
foreach ( $shareTypes as $shareType ) {
2025-04-08 01:20:31 -04:00
try {
$provider = $this -> factory -> getProviderForType ( $shareType );
} catch ( ProviderException $e ) {
continue ;
}
2024-10-14 05:56:58 -04:00
if ( $node instanceof Folder ) {
/* We need to get all shares by this user to get subshares */
$shares = $provider -> getSharesBy ( $userId , $shareType , null , false , - 1 , 0 );
2024-08-07 05:13:35 -04:00
2024-10-14 05:56:58 -04:00
foreach ( $shares as $share ) {
try {
$path = $share -> getNode () -> getPath ();
} catch ( NotFoundException ) {
/* Ignore share of non-existing node */
continue ;
}
2024-10-14 11:23:29 -04:00
if ( $node -> getRelativePath ( $path ) !== null ) {
/* If relative path is not null it means the shared node is the same or in a subfolder */
2024-10-14 05:56:58 -04:00
$reshareRecords [] = $share ;
}
}
} else {
$shares = $provider -> getSharesBy ( $userId , $shareType , $node , false , - 1 , 0 );
2024-08-07 05:13:35 -04:00
foreach ( $shares as $child ) {
$reshareRecords [] = $child ;
}
}
}
}
foreach ( $reshareRecords as $child ) {
try {
2024-09-16 12:13:54 -04:00
/* Check if the share is still valid (means the resharer still has access to the file through another mean) */
2024-08-07 05:13:35 -04:00
$this -> generalCreateChecks ( $child );
} catch ( GenericShareException $e ) {
2024-09-13 04:00:53 -04:00
/* The check is invalid, promote it to a direct share from the sharer of parent share */
$this -> logger -> debug ( 'Promote reshare because of exception ' . $e -> getMessage (), [ 'exception' => $e , 'fullId' => $child -> getFullId ()]);
try {
$child -> setSharedBy ( $share -> getSharedBy ());
$this -> updateShare ( $child );
} catch ( GenericShareException | \InvalidArgumentException $e ) {
$this -> logger -> warning ( 'Failed to promote reshare because of exception ' . $e -> getMessage (), [ 'exception' => $e , 'fullId' => $child -> getFullId ()]);
}
2024-08-07 05:13:35 -04:00
}
}
}
2015-10-30 08:09:07 -04:00
/**
* Delete a share
*
2020-06-24 10:49:16 -04:00
* @ param IShare $share
2015-10-30 08:10:08 -04:00
* @ throws ShareNotFound
2016-02-08 15:08:03 -05:00
* @ throws \InvalidArgumentException
2015-10-30 08:09:07 -04:00
*/
2020-06-24 10:49:16 -04:00
public function deleteShare ( IShare $share ) {
2016-02-08 15:08:03 -05:00
try {
2016-02-11 04:51:58 -05:00
$share -> getFullId ();
2016-02-08 15:08:03 -05:00
} catch ( \UnexpectedValueException $e ) {
2024-09-06 13:21:07 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Share does not have a full ID' ));
2016-02-08 15:08:03 -05:00
}
2015-11-10 08:14:49 -05:00
2023-07-27 16:25:26 -04:00
$this -> dispatcher -> dispatchTyped ( new BeforeShareDeletedEvent ( $share ));
2015-11-10 08:14:49 -05:00
// Get all children and delete them as well
2023-07-27 16:25:26 -04:00
$this -> deleteChildren ( $share );
2015-11-10 08:14:49 -05:00
// Do the actual delete
2016-01-13 08:21:55 -05:00
$provider = $this -> factory -> getProviderForType ( $share -> getShareType ());
2016-01-08 08:31:28 -05:00
$provider -> delete ( $share );
2015-11-10 08:14:49 -05:00
2023-07-27 16:25:26 -04:00
$this -> dispatcher -> dispatchTyped ( new ShareDeletedEvent ( $share ));
2024-08-22 11:03:07 -04:00
2024-09-13 04:00:53 -04:00
// Promote reshares of the deleted share
$this -> promoteReshares ( $share );
2015-10-30 08:09:07 -04:00
}
2015-12-03 04:51:41 -05:00
2016-01-21 08:31:09 -05: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 .
*
2020-06-24 10:49:16 -04:00
* @ param IShare $share
2016-02-02 15:02:09 -05:00
* @ param string $recipientId
2016-01-21 08:31:09 -05:00
*/
2020-06-24 10:49:16 -04:00
public function deleteFromSelf ( IShare $share , $recipientId ) {
2021-04-23 14:20:56 -04:00
[ $providerId ,] = $this -> splitFullId ( $share -> getFullId ());
2016-01-21 08:31:09 -05:00
$provider = $this -> factory -> getProvider ( $providerId );
2016-02-02 15:02:09 -05:00
$provider -> deleteFromSelf ( $share , $recipientId );
2023-07-27 16:25:26 -04:00
$event = new ShareDeletedFromSelfEvent ( $share );
$this -> dispatcher -> dispatchTyped ( $event );
2016-01-21 08:31:09 -05:00
}
2018-06-19 03:20:35 -04:00
public function restoreShare ( IShare $share , string $recipientId ) : IShare {
2021-04-23 14:20:56 -04:00
[ $providerId ,] = $this -> splitFullId ( $share -> getFullId ());
2018-06-19 03:20:35 -04:00
$provider = $this -> factory -> getProvider ( $providerId );
return $provider -> restore ( $share , $recipientId );
}
2016-01-29 04:27:39 -05:00
/**
* @ inheritdoc
*/
2020-06-24 10:49:16 -04:00
public function moveShare ( IShare $share , $recipientId ) {
2020-10-20 07:58:01 -04:00
if ( $share -> getShareType () === IShare :: TYPE_LINK
|| $share -> getShareType () === IShare :: TYPE_EMAIL ) {
2023-08-30 09:22:45 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Cannot change target of link share' ));
2016-01-29 04:27:39 -05:00
}
2020-06-24 10:49:16 -04:00
if ( $share -> getShareType () === IShare :: TYPE_USER && $share -> getSharedWith () !== $recipientId ) {
2024-09-05 12:06:22 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Invalid share recipient' ));
2016-01-29 04:27:39 -05:00
}
2020-06-24 10:49:16 -04:00
if ( $share -> getShareType () === IShare :: TYPE_GROUP ) {
2016-02-02 15:02:09 -05:00
$sharedWith = $this -> groupManager -> get ( $share -> getSharedWith ());
2017-01-19 09:02:46 -05:00
if ( is_null ( $sharedWith )) {
2023-08-30 09:22:45 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Group "%s" does not exist' , [ $share -> getSharedWith ()]));
2017-01-19 09:02:46 -05:00
}
2016-02-02 15:02:09 -05:00
$recipient = $this -> userManager -> get ( $recipientId );
if ( ! $sharedWith -> inGroup ( $recipient )) {
2024-09-05 12:06:22 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Invalid share recipient' ));
2016-02-02 15:02:09 -05:00
}
}
2021-04-23 14:20:56 -04:00
[ $providerId ,] = $this -> splitFullId ( $share -> getFullId ());
2016-01-29 04:27:39 -05:00
$provider = $this -> factory -> getProvider ( $providerId );
2020-06-26 04:57:14 -04:00
return $provider -> move ( $share , $recipientId );
2016-01-29 04:27:39 -05:00
}
2022-06-23 10:32:52 -04:00
public function getSharesInFolder ( $userId , Folder $node , $reshares = false , $shallow = true ) {
2016-06-18 16:04:56 -04:00
$providers = $this -> factory -> getAllProviders ();
2024-03-07 05:59:53 -05:00
if ( ! $shallow ) {
2024-09-03 15:49:04 -04:00
throw new \Exception ( 'non-shallow getSharesInFolder is no longer supported' );
2024-03-07 05:59:53 -05:00
}
2016-06-18 16:04:56 -04:00
2024-10-22 07:19:18 -04:00
$isOwnerless = $node -> getMountPoint () instanceof IShareOwnerlessMount ;
2016-11-01 07:15:14 -04:00
2024-10-22 07:19:18 -04:00
$shares = [];
foreach ( $providers as $provider ) {
if ( $isOwnerless ) {
foreach ( $node -> getDirectoryListing () as $childNode ) {
$data = $provider -> getSharesByPath ( $childNode );
$fid = $childNode -> getId ();
$shares [ $fid ] ? ? = [];
$shares [ $fid ] = array_merge ( $shares [ $fid ], $data );
}
} else {
foreach ( $provider -> getSharesInFolder ( $userId , $node , $reshares ) as $fid => $data ) {
$shares [ $fid ] ? ? = [];
$shares [ $fid ] = array_merge ( $shares [ $fid ], $data );
}
2016-11-01 07:15:14 -04:00
}
2024-10-22 07:19:18 -04:00
}
return $shares ;
2016-06-18 16:04:56 -04:00
}
2015-12-03 04:51:41 -05:00
/**
2016-03-11 02:51:07 -05:00
* @ inheritdoc
2015-12-03 04:51:41 -05:00
*/
2024-11-14 01:41:41 -05:00
public function getSharesBy ( $userId , $shareType , $path = null , $reshares = false , $limit = 50 , $offset = 0 , bool $onlyValid = true ) {
2015-12-03 04:51:41 -05:00
if ( $path !== null &&
2021-04-23 14:20:56 -04:00
! ( $path instanceof \OCP\Files\File ) &&
! ( $path instanceof \OCP\Files\Folder )) {
2023-08-30 09:22:45 -04:00
throw new \InvalidArgumentException ( $this -> l -> t ( 'Invalid path' ));
2015-12-03 04:51:41 -05:00
}
2016-12-05 13:40:37 -05:00
try {
$provider = $this -> factory -> getProviderForType ( $shareType );
} catch ( ProviderException $e ) {
return [];
}
2015-12-03 04:51:41 -05:00
2024-10-22 07:19:18 -04:00
if ( $path ? -> getMountPoint () instanceof IShareOwnerlessMount ) {
$shares = array_filter ( $provider -> getSharesByPath ( $path ), static fn ( IShare $share ) => $share -> getShareType () === $shareType );
} else {
$shares = $provider -> getSharesBy ( $userId , $shareType , $path , $reshares , $limit , $offset );
}
2016-02-05 14:00:07 -05:00
/*
* Work around so we don ' t return expired shares but still follow
* proper pagination .
*/
2017-03-30 11:03:04 -04:00
$shares2 = [];
2017-03-29 10:50:23 -04:00
2017-03-30 11:03:04 -04:00
while ( true ) {
$added = 0 ;
foreach ( $shares as $share ) {
2024-11-14 01:41:41 -05:00
if ( $onlyValid ) {
try {
$this -> checkShare ( $share );
} catch ( ShareNotFound $e ) {
// Ignore since this basically means the share is deleted
continue ;
}
2016-02-05 14:00:07 -05:00
}
2017-03-30 11:03:04 -04:00
$added ++ ;
$shares2 [] = $share ;
2016-02-05 14:00:07 -05:00
2017-03-30 11:03:04 -04:00
if ( count ( $shares2 ) === $limit ) {
2016-03-08 10:12:17 -05:00
break ;
}
2017-03-30 11:03:04 -04:00
}
2016-03-08 10:12:17 -05:00
2017-09-13 15:42:47 -04: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 11:03:04 -04:00
if ( count ( $shares2 ) === $limit ) {
break ;
}
2016-02-05 14:00:07 -05:00
2017-03-30 11:03:04 -04:00
// If there was no limit on the select we are done
if ( $limit === - 1 ) {
break ;
2016-02-05 14:00:07 -05:00
}
2017-03-30 11:03:04 -04:00
$offset += $added ;
2016-02-05 14:00:07 -05:00
2017-03-30 11:03:04 -04:00
// Fetch again $limit shares
2024-10-22 07:19:18 -04:00
if ( $path ? -> getMountPoint () instanceof IShareOwnerlessMount ) {
// We already fetched all shares, so end here
$shares = [];
} else {
$shares = $provider -> getSharesBy ( $userId , $shareType , $path , $reshares , $limit , $offset );
}
2017-03-29 10:50:23 -04:00
2017-03-30 11:03:04 -04:00
// No more shares means we are done
if ( empty ( $shares )) {
break ;
2017-03-29 10:50:23 -04:00
}
}
2017-03-30 11:03:04 -04:00
$shares = $shares2 ;
2017-03-29 10:50:23 -04:00
2016-02-05 14:00:07 -05:00
return $shares ;
2015-12-03 04:51:41 -05:00
}
2015-10-30 08:09:07 -04:00
/**
2016-01-29 09:26:04 -05:00
* @ inheritdoc
2015-10-30 08:09:07 -04:00
*/
2016-02-02 15:02:09 -05:00
public function getSharedWith ( $userId , $shareType , $node = null , $limit = 50 , $offset = 0 ) {
2016-12-05 13:40:37 -05:00
try {
$provider = $this -> factory -> getProviderForType ( $shareType );
} catch ( ProviderException $e ) {
return [];
}
2015-12-03 04:51:41 -05:00
2017-03-29 10:50:23 -04:00
$shares = $provider -> getSharedWith ( $userId , $shareType , $node , $limit , $offset );
// remove all shares which are already expired
foreach ( $shares as $key => $share ) {
try {
2023-08-03 10:13:38 -04:00
$this -> checkShare ( $share );
2017-03-29 10:50:23 -04:00
} catch ( ShareNotFound $e ) {
unset ( $shares [ $key ]);
}
}
return $shares ;
2015-10-30 08:09:07 -04:00
}
2018-06-19 11:44:20 -04: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 07:53:40 -04:00
$shares = array_filter ( $shares , function ( IShare $share ) {
2018-06-19 11:44:20 -04: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 08:09:07 -04:00
/**
2016-01-29 04:07:28 -05:00
* @ inheritdoc
2015-10-30 08:09:07 -04:00
*/
2024-11-14 01:41:41 -05:00
public function getShareById ( $id , $recipient = null , bool $onlyValid = true ) {
2015-11-10 08:14:49 -05:00
if ( $id === null ) {
throw new ShareNotFound ();
}
2021-01-12 04:15:48 -05:00
[ $providerId , $id ] = $this -> splitFullId ( $id );
2016-12-05 13:40:37 -05:00
try {
$provider = $this -> factory -> getProvider ( $providerId );
} catch ( ProviderException $e ) {
throw new ShareNotFound ();
}
2016-01-08 08:31:28 -05:00
2016-01-29 04:07:28 -05:00
$share = $provider -> getShareById ( $id , $recipient );
2015-10-30 08:10:08 -04:00
2024-11-14 01:41:41 -05:00
if ( $onlyValid ) {
$this -> checkShare ( $share );
}
2016-02-05 09:34:30 -05:00
2015-10-30 08:10:08 -04:00
return $share ;
2015-10-30 08:09:07 -04:00
}
/**
* Get all the shares for a given path
*
* @ param \OCP\Files\Node $path
* @ param int $page
* @ param int $perPage
*
* @ return Share []
*/
2020-10-05 09:12:57 -04:00
public function getSharesByPath ( \OCP\Files\Node $path , $page = 0 , $perPage = 50 ) {
2016-12-07 01:11:32 -05:00
return [];
2015-10-30 08:09:07 -04:00
}
/**
* Get the share by token possible with password
*
* @ param string $token
2020-06-26 04:57:14 -04:00
* @ return IShare
2015-10-30 08:09:07 -04:00
*
2015-11-23 08:06:25 -05:00
* @ throws ShareNotFound
2015-10-30 08:09:07 -04:00
*/
2016-01-14 10:44:59 -05:00
public function getShareByToken ( $token ) {
2021-04-15 09:42:43 -04:00
// tokens cannot be valid local user names
2018-08-16 14:39:51 -04:00
if ( $this -> userManager -> userExists ( $token )) {
throw new ShareNotFound ();
}
2016-12-07 01:11:32 -05:00
$share = null ;
2016-12-05 13:40:37 -05:00
try {
2016-12-22 04:59:27 -05:00
if ( $this -> shareApiAllowLinks ()) {
2020-06-24 10:49:16 -04:00
$provider = $this -> factory -> getProviderForType ( IShare :: TYPE_LINK );
2016-12-22 04:59:27 -05:00
$share = $provider -> getShareByToken ( $token );
2016-12-20 02:52:31 -05:00
}
2016-12-07 01:11:32 -05:00
} catch ( ProviderException $e ) {
2016-04-01 11:02:59 -04:00
} catch ( ShareNotFound $e ) {
}
2016-12-07 01:11:32 -05:00
2016-04-01 11:02:59 -04:00
// If it is not a link share try to fetch a federated share by token
if ( $share === null ) {
2016-12-05 13:40:37 -05:00
try {
2020-06-24 10:49:16 -04:00
$provider = $this -> factory -> getProviderForType ( IShare :: TYPE_REMOTE );
2016-10-26 06:18:39 -04:00
$share = $provider -> getShareByToken ( $token );
2016-12-07 01:11:32 -05:00
} catch ( ProviderException $e ) {
2016-10-26 06:18:39 -04:00
} catch ( ShareNotFound $e ) {
}
}
2016-12-22 04:59:27 -05:00
// If it is not a link share try to fetch a mail share by token
2020-06-24 10:49:16 -04:00
if ( $share === null && $this -> shareProviderExists ( IShare :: TYPE_EMAIL )) {
2016-12-05 13:40:37 -05:00
try {
2020-06-24 10:49:16 -04:00
$provider = $this -> factory -> getProviderForType ( IShare :: TYPE_EMAIL );
2016-12-07 01:11:32 -05:00
$share = $provider -> getShareByToken ( $token );
2016-12-05 13:40:37 -05:00
} catch ( ProviderException $e ) {
2016-12-07 01:11:32 -05:00
} catch ( ShareNotFound $e ) {
2016-12-05 13:40:37 -05:00
}
2016-12-07 01:11:32 -05:00
}
2020-06-24 10:49:16 -04:00
if ( $share === null && $this -> shareProviderExists ( IShare :: TYPE_CIRCLE )) {
2017-08-20 09:21:58 -04:00
try {
2020-06-24 10:49:16 -04:00
$provider = $this -> factory -> getProviderForType ( IShare :: TYPE_CIRCLE );
2017-08-20 09:21:58 -04:00
$share = $provider -> getShareByToken ( $token );
} catch ( ProviderException $e ) {
} catch ( ShareNotFound $e ) {
}
}
2020-06-24 10:49:16 -04:00
if ( $share === null && $this -> shareProviderExists ( IShare :: TYPE_ROOM )) {
2018-08-01 13:04:32 -04:00
try {
2020-06-24 10:49:16 -04:00
$provider = $this -> factory -> getProviderForType ( IShare :: TYPE_ROOM );
2018-08-01 13:04:32 -04:00
$share = $provider -> getShareByToken ( $token );
} catch ( ProviderException $e ) {
} catch ( ShareNotFound $e ) {
}
}
2016-12-07 01:11:32 -05:00
if ( $share === null ) {
2017-06-13 12:44:14 -04:00
throw new ShareNotFound ( $this -> l -> t ( 'The requested share does not exist anymore' ));
2016-04-01 11:02:59 -04:00
}
2016-01-14 10:44:59 -05:00
2023-08-03 10:13:38 -04:00
$this -> checkShare ( $share );
2016-01-14 10:44:59 -05:00
2016-04-01 11:02:59 -04:00
/*
2020-10-20 07:58:01 -04:00
* Reduce the permissions for link or email shares if public upload is not enabled
2016-04-01 11:02:59 -04:00
*/
2020-10-20 07:58:01 -04:00
if (( $share -> getShareType () === IShare :: TYPE_LINK || $share -> getShareType () === IShare :: TYPE_EMAIL )
2022-06-21 08:25:36 -04:00
&& $share -> getNodeType () === 'folder' && ! $this -> shareApiLinkAllowPublicUpload ()) {
2016-04-01 11:02:59 -04:00
$share -> setPermissions ( $share -> getPermissions () & ~ ( \OCP\Constants :: PERMISSION_CREATE | \OCP\Constants :: PERMISSION_UPDATE ));
}
2016-01-14 10:44:59 -05:00
return $share ;
}
2023-08-03 10:13:38 -04:00
/**
* Check expire date and disabled owner
*
* @ throws ShareNotFound
*/
protected function checkShare ( IShare $share ) : void {
2019-08-12 04:56:23 -04:00
if ( $share -> isExpired ()) {
2017-03-29 10:50:23 -04:00
$this -> deleteShare ( $share );
2017-06-13 12:44:14 -04:00
throw new ShareNotFound ( $this -> l -> t ( 'The requested share does not exist anymore' ));
2017-03-29 10:50:23 -04:00
}
2023-08-03 10:13:38 -04:00
if ( $this -> config -> getAppValue ( 'files_sharing' , 'hide_disabled_user_shares' , 'no' ) === 'yes' ) {
$uids = array_unique ([ $share -> getShareOwner (), $share -> getSharedBy ()]);
foreach ( $uids as $uid ) {
$user = $this -> userManager -> get ( $uid );
2023-08-10 11:05:19 -04:00
if ( $user ? -> isEnabled () === false ) {
2023-08-03 10:13:38 -04:00
throw new ShareNotFound ( $this -> l -> t ( 'The requested share comes from a disabled user' ));
}
}
}
2017-03-29 10:50:23 -04:00
}
2016-01-14 10:44:59 -05:00
/**
* Verify the password of a public share
*
2020-06-24 10:49:16 -04:00
* @ param IShare $share
2022-10-17 06:38:21 -04:00
* @ param ? string $password
2016-01-14 10:44:59 -05:00
* @ return bool
*/
2020-06-24 10:49:16 -04:00
public function checkPassword ( IShare $share , $password ) {
2016-01-14 10:44:59 -05:00
2023-12-18 13:03:46 -05:00
// if there is no password on the share object / passsword is null, there is nothing to check
2016-01-14 10:44:59 -05:00
if ( $password === null || $share -> getPassword () === null ) {
return false ;
}
2022-02-05 14:49:17 -05:00
// Makes sure password hasn't expired
$expirationTime = $share -> getPasswordExpirationTime ();
if ( $expirationTime !== null && $expirationTime < new \DateTime ()) {
return false ;
}
2016-01-14 10:44:59 -05:00
$newHash = '' ;
if ( ! $this -> hasher -> verify ( $password , $share -> getPassword (), $newHash )) {
return false ;
}
if ( ! empty ( $newHash )) {
2016-02-01 15:46:32 -05:00
$share -> setPassword ( $newHash );
$provider = $this -> factory -> getProviderForType ( $share -> getShareType ());
$provider -> update ( $share );
2016-01-14 10:44:59 -05:00
}
return true ;
2015-10-30 08:09:07 -04:00
}
2016-04-04 06:28:19 -04:00
/**
* @ inheritdoc
*/
public function userDeleted ( $uid ) {
2020-06-24 10:49:16 -04:00
$types = [ IShare :: TYPE_USER , IShare :: TYPE_GROUP , IShare :: TYPE_LINK , IShare :: TYPE_REMOTE , IShare :: TYPE_EMAIL ];
2016-04-04 06:28:19 -04:00
foreach ( $types as $type ) {
2016-12-05 13:40:37 -05:00
try {
$provider = $this -> factory -> getProviderForType ( $type );
} catch ( ProviderException $e ) {
continue ;
}
2016-04-04 06:28:19 -04:00
$provider -> userDeleted ( $uid , $type );
}
}
2016-04-12 03:46:25 -04:00
/**
* @ inheritdoc
*/
public function groupDeleted ( $gid ) {
2025-02-24 05:28:28 -05:00
foreach ([ IShare :: TYPE_GROUP , IShare :: TYPE_REMOTE_GROUP ] as $type ) {
try {
$provider = $this -> factory -> getProviderForType ( $type );
} catch ( ProviderException $e ) {
continue ;
}
$provider -> groupDeleted ( $gid );
}
2020-01-24 10:34:07 -05:00
$excludedGroups = $this -> config -> getAppValue ( 'core' , 'shareapi_exclude_groups_list' , '' );
if ( $excludedGroups === '' ) {
return ;
}
$excludedGroups = json_decode ( $excludedGroups , true );
if ( json_last_error () !== JSON_ERROR_NONE ) {
return ;
}
$excludedGroups = array_diff ( $excludedGroups , [ $gid ]);
$this -> config -> setAppValue ( 'core' , 'shareapi_exclude_groups_list' , json_encode ( $excludedGroups ));
2016-04-12 03:46:25 -04:00
}
2016-04-13 09:00:12 -04:00
/**
* @ inheritdoc
*/
public function userDeletedFromGroup ( $uid , $gid ) {
2025-02-24 05:28:28 -05:00
foreach ([ IShare :: TYPE_GROUP , IShare :: TYPE_REMOTE_GROUP ] as $type ) {
try {
$provider = $this -> factory -> getProviderForType ( $type );
} catch ( ProviderException $e ) {
continue ;
}
$provider -> userDeletedFromGroup ( $uid , $gid );
}
2016-04-13 09:00:12 -04:00
}
2015-10-30 08:09:07 -04:00
/**
* Get access list to a path . This means
2016-09-09 06:53:17 -04:00
* all the users that can access a given path .
2015-10-30 08:09:07 -04:00
*
* Consider :
* - root
2017-04-11 06:40:36 -04:00
* |- folder1 ( 23 )
* |- folder2 ( 32 )
* |- fileA ( 42 )
2015-10-30 08:09:07 -04:00
*
2022-05-27 10:40:54 -04:00
* fileA is shared with user1 and user1 @ server1 and email1 @ maildomain1
2016-09-09 06:53:17 -04:00
* folder2 is shared with group2 ( user4 is a member of group2 )
2017-04-11 06:40:36 -04:00
* folder1 is shared with user2 ( renamed to " folder (1) " ) and user2 @ server2
2022-05-27 10:40:54 -04:00
* and email2 @ maildomain2
2015-10-30 08:09:07 -04:00
*
2017-04-11 06:40:36 -04:00
* Then the access list to '/folder1/folder2/fileA' with $currentAccess is :
2015-10-30 08:09:07 -04:00
* [
2017-04-11 06:40:36 -04: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
2022-05-27 10:40:54 -04:00
* mail => [
2022-05-27 11:42:26 -04:00
* 'email1@maildomain1' => [ 'node_id' => 42 , 'token' => 'aBcDeFg' ],
* 'email2@maildomain2' => [ 'node_id' => 23 , 'token' => 'hIjKlMn' ],
* ]
2017-04-11 06:40:36 -04:00
* ]
*
* The access list to '/folder1/folder2/fileA' ** without ** $currentAccess is :
* [
* users => [ 'user1' , 'user2' , 'user4' ],
* remote => bool ,
2016-09-09 06:53:17 -04:00
* public => bool
2022-05-27 10:40:54 -04:00
* mail => [ 'email1@maildomain1' , 'email2@maildomain2' ]
2015-10-30 08:09:07 -04:00
* ]
*
2016-10-19 15:27:07 -04:00
* This is required for encryption / activity
2015-10-30 08:09:07 -04:00
*
* @ param \OCP\Files\Node $path
2016-10-19 15:27:07 -04:00
* @ param bool $recursive Should we check all parent folders as well
2017-08-30 04:56:02 -04:00
* @ param bool $currentAccess Ensure the recipient has access to the file ( e . g . did not unshare it )
2016-09-09 06:53:17 -04:00
* @ return array
2015-10-30 08:09:07 -04:00
*/
2016-12-22 15:44:21 -05:00
public function getAccessList ( \OCP\Files\Node $path , $recursive = true , $currentAccess = false ) {
2019-03-19 10:38:52 -04:00
$owner = $path -> getOwner ();
if ( $owner === null ) {
return [];
}
$owner = $owner -> getUID ();
2016-09-09 06:53:17 -04:00
2017-04-11 06:40:36 -04:00
if ( $currentAccess ) {
2022-05-27 10:40:54 -04:00
$al = [ 'users' => [], 'remote' => [], 'public' => false , 'mail' => []];
2017-04-11 06:40:36 -04:00
} else {
2022-05-27 10:40:54 -04:00
$al = [ 'users' => [], 'remote' => false , 'public' => false , 'mail' => []];
2017-04-11 06:40:36 -04:00
}
2016-12-22 15:44:21 -05:00
if ( ! $this -> userManager -> userExists ( $owner )) {
return $al ;
}
2021-05-20 06:13:04 -04:00
//Get node for the owner and correct the owner in case of external storage
2016-09-09 06:53:17 -04:00
$userFolder = $this -> rootFolder -> getUserFolder ( $owner );
2017-04-19 09:11:54 -04:00
if ( $path -> getId () !== $userFolder -> getId () && ! $userFolder -> isSubNode ( $path )) {
2024-02-09 03:54:52 -05:00
$path = $userFolder -> getFirstNodeById ( $path -> getId ());
2021-10-18 15:41:01 -04:00
if ( $path === null || $path -> getOwner () === null ) {
2019-05-31 17:16:06 -04:00
return [];
}
2019-02-14 04:59:17 -05:00
$owner = $path -> getOwner () -> getUID ();
2016-10-19 15:27:07 -04:00
}
2016-09-09 06:53:17 -04:00
$providers = $this -> factory -> getAllProviders ();
2016-12-22 15:44:21 -05:00
/** @var Node[] $nodes */
$nodes = [];
2017-04-13 06:58:29 -04:00
if ( $currentAccess ) {
$ownerPath = $path -> getPath ();
2017-04-19 09:11:54 -04:00
$ownerPath = explode ( '/' , $ownerPath , 4 );
if ( count ( $ownerPath ) < 4 ) {
$ownerPath = '' ;
} else {
$ownerPath = $ownerPath [ 3 ];
}
2017-04-13 06:58:29 -04:00
$al [ 'users' ][ $owner ] = [
'node_id' => $path -> getId (),
'node_path' => '/' . $ownerPath ,
];
} else {
$al [ 'users' ][] = $owner ;
}
2016-09-09 06:53:17 -04:00
// Collect all the shares
2016-10-19 15:27:07 -04:00
while ( $path -> getPath () !== $userFolder -> getPath ()) {
2016-12-22 15:44:21 -05:00
$nodes [] = $path ;
2016-10-19 15:27:07 -04:00
if ( ! $recursive ) {
break ;
}
2016-09-09 06:53:17 -04:00
$path = $path -> getParent ();
}
2016-12-22 15:44:21 -05:00
foreach ( $providers as $provider ) {
$tmp = $provider -> getAccessList ( $nodes , $currentAccess );
2016-09-09 06:53:17 -04:00
2017-01-04 02:59:43 -05:00
foreach ( $tmp as $k => $v ) {
if ( isset ( $al [ $k ])) {
if ( is_array ( $al [ $k ])) {
2017-11-28 10:38:16 -05: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 02:59:43 -05:00
} else {
$al [ $k ] = $al [ $k ] || $v ;
}
} else {
$al [ $k ] = $v ;
}
}
2016-09-09 06:53:17 -04:00
}
2016-12-22 15:44:21 -05:00
return $al ;
2015-10-30 08:09:07 -04:00
}
2015-12-15 03:54:12 -05:00
/**
* Create a new share
2020-06-24 10:49:16 -04:00
*
* @ return IShare
2015-12-15 03:54:12 -05:00
*/
public function newShare () {
2016-05-11 14:48:27 -04:00
return new \OC\Share20\Share ( $this -> rootFolder , $this -> userManager );
2015-12-15 03:54:12 -05: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 () {
2021-04-23 14:20:56 -04:00
if ( $this -> config -> getAppValue ( 'core' , 'shareapi_allow_links' , 'yes' ) !== 'yes' ) {
return false ;
}
$user = $this -> userSession -> getUser ();
if ( $user ) {
$excludedGroups = json_decode ( $this -> config -> getAppValue ( 'core' , 'shareapi_allow_links_exclude_groups' , '[]' ));
if ( $excludedGroups ) {
$userGroups = $this -> groupManager -> getUserGroupIds ( $user );
return ! ( bool ) array_intersect ( $excludedGroups , $userGroups );
}
}
return true ;
2015-12-15 03:54:12 -05:00
}
/**
* Is password on public link requires
*
2022-02-15 11:45:09 -05:00
* @ param bool Check group membership exclusion
2015-12-15 03:54:12 -05:00
* @ return bool
*/
2022-02-15 11:45:09 -05:00
public function shareApiLinkEnforcePassword ( bool $checkGroupMembership = true ) {
$excludedGroups = $this -> config -> getAppValue ( 'core' , 'shareapi_enforce_links_password_excluded_groups' , '' );
if ( $excludedGroups !== '' && $checkGroupMembership ) {
$excludedGroups = json_decode ( $excludedGroups );
$user = $this -> userSession -> getUser ();
if ( $user ) {
$userGroups = $this -> groupManager -> getUserGroupIds ( $user );
if (( bool ) array_intersect ( $excludedGroups , $userGroups )) {
return false ;
}
}
}
2015-12-15 03:54:12 -05:00
return $this -> config -> getAppValue ( 'core' , 'shareapi_enforce_links_password' , 'no' ) === 'yes' ;
}
/**
2019-10-28 14:22:05 -04:00
* Is default link expire date enabled
2015-12-15 03:54:12 -05:00
*
* @ return bool
*/
public function shareApiLinkDefaultExpireDate () {
return $this -> config -> getAppValue ( 'core' , 'shareapi_default_expire_date' , 'no' ) === 'yes' ;
}
/**
2019-10-28 14:22:05 -04:00
* Is default link expire date enforced
2015-12-15 03:54:12 -05:00
* `
2021-04-23 14:20:56 -04:00
*
2015-12-15 03:54:12 -05:00
* @ return bool
*/
public function shareApiLinkDefaultExpireDateEnforced () {
2016-02-26 05:38:06 -05:00
return $this -> shareApiLinkDefaultExpireDate () &&
$this -> config -> getAppValue ( 'core' , 'shareapi_enforce_expire_date' , 'no' ) === 'yes' ;
2015-12-15 03:54:12 -05:00
}
2019-10-28 14:22:05 -04:00
2015-12-15 03:54:12 -05:00
/**
2019-10-28 14:22:05 -04:00
* Number of default link expire days
2021-04-23 14:20:56 -04:00
*
2015-12-15 03:54:12 -05:00
* @ return int
*/
public function shareApiLinkDefaultExpireDays () {
return ( int ) $this -> config -> getAppValue ( 'core' , 'shareapi_expire_after_n_days' , '7' );
}
2019-10-28 14:22:05 -04: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' ;
}
2021-03-25 12:32:49 -04:00
/**
* Is default remote expire date enabled
*
* @ return bool
*/
public function shareApiRemoteDefaultExpireDate () : bool {
return $this -> config -> getAppValue ( 'core' , 'shareapi_default_remote_expire_date' , 'no' ) === 'yes' ;
}
2019-10-28 14:22:05 -04:00
/**
* Is default expire date enforced
2021-03-25 12:32:49 -04:00
*
2019-10-28 14:22:05 -04:00
* @ return bool
*/
public function shareApiInternalDefaultExpireDateEnforced () : bool {
2019-10-29 16:10:50 -04:00
return $this -> shareApiInternalDefaultExpireDate () &&
2019-10-28 14:22:05 -04:00
$this -> config -> getAppValue ( 'core' , 'shareapi_enforce_internal_expire_date' , 'no' ) === 'yes' ;
}
2021-03-25 12:32:49 -04:00
/**
* Is default expire date enforced for remote shares
*
* @ return bool
*/
public function shareApiRemoteDefaultExpireDateEnforced () : bool {
return $this -> shareApiRemoteDefaultExpireDate () &&
$this -> config -> getAppValue ( 'core' , 'shareapi_enforce_remote_expire_date' , 'no' ) === 'yes' ;
}
2019-10-28 14:22:05 -04:00
/**
* Number of default expire days
2021-04-23 14:20:56 -04:00
*
2019-10-28 14:22:05 -04:00
* @ return int
*/
public function shareApiInternalDefaultExpireDays () : int {
return ( int ) $this -> config -> getAppValue ( 'core' , 'shareapi_internal_expire_after_n_days' , '7' );
}
2021-03-25 12:32:49 -04:00
/**
* Number of default expire days for remote shares
2021-04-23 14:20:56 -04:00
*
2021-03-25 12:32:49 -04:00
* @ return int
*/
public function shareApiRemoteDefaultExpireDays () : int {
return ( int ) $this -> config -> getAppValue ( 'core' , 'shareapi_remote_expire_after_n_days' , '7' );
}
2015-12-15 03:54:12 -05: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
2021-04-23 14:20:56 -04:00
*
2015-12-15 03:54:12 -05:00
* @ return bool
*/
public function shareWithGroupMembersOnly () {
return $this -> config -> getAppValue ( 'core' , 'shareapi_only_share_with_group_members' , 'no' ) === 'yes' ;
}
2023-12-27 06:56:23 -05:00
/**
* If shareWithGroupMembersOnly is enabled , return an optional
* list of groups that must be excluded from the principle of
* belonging to the same group .
*
* @ return array
*/
public function shareWithGroupMembersOnlyExcludeGroupsList () {
if ( ! $this -> shareWithGroupMembersOnly ()) {
return [];
}
$excludeGroups = $this -> config -> getAppValue ( 'core' , 'shareapi_only_share_with_group_members_exclude_group_list' , '' );
return json_decode ( $excludeGroups , true ) ? ? [];
}
2016-03-18 11:34:23 -04:00
/**
* Check if users can share with groups
2021-04-23 14:20:56 -04:00
*
2016-03-18 11:34:23 -04:00
* @ return bool
*/
public function allowGroupSharing () {
return $this -> config -> getAppValue ( 'core' , 'shareapi_allow_group_sharing' , 'yes' ) === 'yes' ;
}
2015-12-15 03:54:12 -05:00
2020-02-20 12:51:35 -05: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' ;
}
2021-03-09 15:48:48 -05:00
public function limitEnumerationToPhone () : bool {
return $this -> allowEnumeration () &&
$this -> config -> getAppValue ( 'core' , 'shareapi_restrict_user_enumeration_to_phone' , 'no' ) === 'yes' ;
}
2021-03-10 11:18:44 -05:00
public function allowEnumerationFullMatch () : bool {
return $this -> config -> getAppValue ( 'core' , 'shareapi_restrict_user_enumeration_full_match' , 'yes' ) === 'yes' ;
}
2022-04-19 06:54:28 -04:00
public function matchEmail () : bool {
return $this -> config -> getAppValue ( 'core' , 'shareapi_restrict_user_enumeration_full_match_email' , 'yes' ) === 'yes' ;
}
2022-04-13 09:07:27 -04:00
public function ignoreSecondDisplayName () : bool {
2022-06-23 11:51:03 -04:00
return $this -> config -> getAppValue ( 'core' , 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn' , 'no' ) === 'yes' ;
2022-04-13 09:07:27 -04:00
}
2024-12-16 18:53:12 -05:00
public function allowCustomTokens () : bool {
return $this -> appConfig -> getValueBool ( 'core' , 'shareapi_allow_custom_tokens' , false );
}
2021-11-05 05:44:51 -04:00
public function currentUserCanEnumerateTargetUser ( ? IUser $currentUser , IUser $targetUser ) : bool {
if ( $this -> allowEnumerationFullMatch ()) {
return true ;
}
if ( ! $this -> allowEnumeration ()) {
return false ;
}
if ( ! $this -> limitEnumerationToPhone () && ! $this -> limitEnumerationToGroups ()) {
// Enumeration is enabled and not restricted: OK
return true ;
}
if ( ! $currentUser instanceof IUser ) {
// Enumeration restrictions require an account
return false ;
}
// Enumeration is limited to phone match
if ( $this -> limitEnumerationToPhone () && $this -> knownUserService -> isKnownToUser ( $currentUser -> getUID (), $targetUser -> getUID ())) {
return true ;
}
// Enumeration is limited to groups
if ( $this -> limitEnumerationToGroups ()) {
$currentUserGroupIds = $this -> groupManager -> getUserGroupIds ( $currentUser );
$targetUserGroupIds = $this -> groupManager -> getUserGroupIds ( $targetUser );
if ( ! empty ( array_intersect ( $currentUserGroupIds , $targetUserGroupIds ))) {
return true ;
}
}
return false ;
}
2015-12-15 03:54:12 -05:00
/**
* Copied from \OC_Util :: isSharingDisabledForUser
*
2022-07-27 08:51:42 -04:00
* TODO : Deprecate function from OC_Util
2015-12-15 03:54:12 -05:00
*
2016-02-02 15:02:09 -05:00
* @ param string $userId
2015-12-15 03:54:12 -05:00
* @ return bool
*/
2016-02-02 15:02:09 -05:00
public function sharingDisabledForUser ( $userId ) {
2023-08-15 12:21:04 -04:00
return $this -> shareDisableChecker -> sharingDisabledForUser ( $userId );
2015-12-15 03:54:12 -05:00
}
2016-02-04 05:13:06 -05:00
/**
* @ inheritdoc
*/
public function outgoingServer2ServerSharesAllowed () {
return $this -> config -> getAppValue ( 'files_sharing' , 'outgoing_server2server_share_enabled' , 'yes' ) === 'yes' ;
}
2018-06-13 08:19:59 -04:00
/**
* @ inheritdoc
*/
public function outgoingServer2ServerGroupSharesAllowed () {
return $this -> config -> getAppValue ( 'files_sharing' , 'outgoing_server2server_group_share_enabled' , 'no' ) === 'yes' ;
}
2016-10-24 11:03:36 -04:00
/**
* @ inheritdoc
*/
public function shareProviderExists ( $shareType ) {
try {
$this -> factory -> getProviderForType ( $shareType );
} catch ( ProviderException $e ) {
return false ;
}
return true ;
}
2020-12-03 09:51:58 -05:00
public function registerShareProvider ( string $shareProviderClass ) : void {
$this -> factory -> registerProvider ( $shareProviderClass );
}
2019-10-29 11:56:06 -04:00
public function getAllShares () : iterable {
$providers = $this -> factory -> getAllProviders ();
foreach ( $providers as $provider ) {
yield from $provider -> getAllShares ();
}
}
2024-12-16 18:53:12 -05:00
public function generateToken () : string {
// Initial token length
$tokenLength = \OC\Share\Helper :: getTokenLength ();
do {
$tokenExists = false ;
for ( $i = 0 ; $i <= 2 ; $i ++ ) {
// Generate a new token
$token = $this -> secureRandom -> generate (
$tokenLength ,
ISecureRandom :: CHAR_HUMAN_READABLE ,
);
try {
// Try to fetch a share with the generated token
$this -> getShareByToken ( $token );
$tokenExists = true ; // Token exists, we need to try again
} catch ( ShareNotFound $e ) {
// Token is unique, exit the loop
$tokenExists = false ;
break ;
}
}
// If we've reached the maximum attempts and the token still exists, increase the token length
if ( $tokenExists ) {
$tokenLength ++ ;
// Check if the token length exceeds the maximum allowed length
if ( $tokenLength > \OC\Share\Constants :: MAX_TOKEN_LENGTH ) {
throw new ShareTokenException ( 'Unable to generate a unique share token. Maximum token length exceeded.' );
}
}
} while ( $tokenExists );
return $token ;
}
2015-10-30 08:09:07 -04:00
}