Completed
Push — master ( 86c2dd...68b910 )
by
unknown
47:43
created
tests/lib/Share20/ManagerTest.php 1 patch
Indentation   +4848 added lines, -4848 removed lines patch added patch discarded remove patch
@@ -64,10 +64,10 @@  discard block
 block discarded – undo
64 64
 use Psr\Log\LoggerInterface;
65 65
 
66 66
 class DummyShareManagerListener {
67
-	public function post() {
68
-	}
69
-	public function listener() {
70
-	}
67
+    public function post() {
68
+    }
69
+    public function listener() {
70
+    }
71 71
 }
72 72
 
73 73
 /**
@@ -77,5039 +77,5039 @@  discard block
 block discarded – undo
77 77
  */
78 78
 #[\PHPUnit\Framework\Attributes\Group('DB')]
79 79
 class ManagerTest extends \Test\TestCase {
80
-	/** @var Manager */
81
-	protected $manager;
82
-	/** @var LoggerInterface|MockObject */
83
-	protected $logger;
84
-	/** @var IConfig|MockObject */
85
-	protected $config;
86
-	/** @var ISecureRandom|MockObject */
87
-	protected $secureRandom;
88
-	/** @var IHasher|MockObject */
89
-	protected $hasher;
90
-	/** @var IShareProvider|MockObject */
91
-	protected $defaultProvider;
92
-	/** @var IMountManager|MockObject */
93
-	protected $mountManager;
94
-	/** @var IGroupManager|MockObject */
95
-	protected $groupManager;
96
-	/** @var IL10N|MockObject */
97
-	protected $l;
98
-	/** @var IFactory|MockObject */
99
-	protected $l10nFactory;
100
-	/** @var DummyFactory */
101
-	protected $factory;
102
-	/** @var IUserManager|MockObject */
103
-	protected $userManager;
104
-	/** @var IRootFolder | MockObject */
105
-	protected $rootFolder;
106
-	/** @var IEventDispatcher|MockObject */
107
-	protected $dispatcher;
108
-	/** @var IMailer|MockObject */
109
-	protected $mailer;
110
-	/** @var IURLGenerator|MockObject */
111
-	protected $urlGenerator;
112
-	/** @var \OC_Defaults|MockObject */
113
-	protected $defaults;
114
-	/** @var IUserSession|MockObject */
115
-	protected $userSession;
116
-	/** @var KnownUserService|MockObject */
117
-	protected $knownUserService;
118
-	/** @var ShareDisableChecker|MockObject */
119
-	protected $shareDisabledChecker;
120
-	private DateTimeZone $timezone;
121
-	/** @var IDateTimeZone|MockObject */
122
-	protected $dateTimeZone;
123
-	/** @var IAppConfig|MockObject */
124
-	protected $appConfig;
125
-
126
-	protected function setUp(): void {
127
-		$this->logger = $this->createMock(LoggerInterface::class);
128
-		$this->config = $this->createMock(IConfig::class);
129
-		$this->secureRandom = $this->createMock(ISecureRandom::class);
130
-		$this->hasher = $this->createMock(IHasher::class);
131
-		$this->mountManager = $this->createMock(IMountManager::class);
132
-		$this->groupManager = $this->createMock(IGroupManager::class);
133
-		$this->userManager = $this->createMock(IUserManager::class);
134
-		$this->rootFolder = $this->createMock(IRootFolder::class);
135
-		$this->mailer = $this->createMock(IMailer::class);
136
-		$this->urlGenerator = $this->createMock(IURLGenerator::class);
137
-		$this->defaults = $this->createMock(\OC_Defaults::class);
138
-		$this->dispatcher = $this->createMock(IEventDispatcher::class);
139
-		$this->userSession = $this->createMock(IUserSession::class);
140
-		$this->knownUserService = $this->createMock(KnownUserService::class);
141
-
142
-		$this->shareDisabledChecker = new ShareDisableChecker($this->config, $this->userManager, $this->groupManager);
143
-		$this->dateTimeZone = $this->createMock(IDateTimeZone::class);
144
-		$this->timezone = new \DateTimeZone('Pacific/Auckland');
145
-		$this->dateTimeZone->method('getTimeZone')->willReturnCallback(fn () => $this->timezone);
146
-
147
-		$this->appConfig = $this->createMock(IAppConfig::class);
148
-
149
-		$this->l10nFactory = $this->createMock(IFactory::class);
150
-		$this->l = $this->createMock(IL10N::class);
151
-		$this->l->method('t')
152
-			->willReturnCallback(function ($text, $parameters = []) {
153
-				return vsprintf($text, $parameters);
154
-			});
155
-		$this->l->method('n')
156
-			->willReturnCallback(function ($singular, $plural, $count, $parameters = []) {
157
-				return vsprintf(str_replace('%n', $count, ($count === 1) ? $singular : $plural), $parameters);
158
-			});
159
-		$this->l10nFactory->method('get')->willReturn($this->l);
160
-
161
-		$this->factory = new DummyFactory(\OC::$server);
162
-
163
-		$this->manager = $this->createManager($this->factory);
164
-
165
-		$this->defaultProvider = $this->createMock(DefaultShareProvider::class);
166
-		$this->defaultProvider->method('identifier')->willReturn('default');
167
-		$this->factory->setProvider($this->defaultProvider);
168
-	}
169
-
170
-	private function createManager(IProviderFactory $factory): Manager {
171
-		return new Manager(
172
-			$this->logger,
173
-			$this->config,
174
-			$this->secureRandom,
175
-			$this->hasher,
176
-			$this->mountManager,
177
-			$this->groupManager,
178
-			$this->l10nFactory,
179
-			$factory,
180
-			$this->userManager,
181
-			$this->rootFolder,
182
-			$this->mailer,
183
-			$this->urlGenerator,
184
-			$this->defaults,
185
-			$this->dispatcher,
186
-			$this->userSession,
187
-			$this->knownUserService,
188
-			$this->shareDisabledChecker,
189
-			$this->dateTimeZone,
190
-			$this->appConfig,
191
-		);
192
-	}
193
-
194
-	/**
195
-	 * @return MockBuilder
196
-	 */
197
-	private function createManagerMock() {
198
-		return $this->getMockBuilder(Manager::class)
199
-			->setConstructorArgs([
200
-				$this->logger,
201
-				$this->config,
202
-				$this->secureRandom,
203
-				$this->hasher,
204
-				$this->mountManager,
205
-				$this->groupManager,
206
-				$this->l10nFactory,
207
-				$this->factory,
208
-				$this->userManager,
209
-				$this->rootFolder,
210
-				$this->mailer,
211
-				$this->urlGenerator,
212
-				$this->defaults,
213
-				$this->dispatcher,
214
-				$this->userSession,
215
-				$this->knownUserService,
216
-				$this->shareDisabledChecker,
217
-				$this->dateTimeZone,
218
-				$this->appConfig,
219
-			]);
220
-	}
221
-
222
-	private function createFolderMock(string $folderPath): MockObject&Folder {
223
-		$folder = $this->createMock(Folder::class);
224
-		$folder->method('getPath')->willReturn($folderPath);
225
-		$folder->method('getRelativePath')->willReturnCallback(
226
-			fn (string $path): ?string => PathHelper::getRelativePath($folderPath, $path)
227
-		);
228
-		return $folder;
229
-	}
230
-
231
-	public function testDeleteNoShareId(): void {
232
-		$this->expectException(\InvalidArgumentException::class);
233
-
234
-		$share = $this->manager->newShare();
235
-
236
-		$this->manager->deleteShare($share);
237
-	}
238
-
239
-	public static function dataTestDelete(): array {
240
-		return [
241
-			[IShare::TYPE_USER, 'sharedWithUser'],
242
-			[IShare::TYPE_GROUP, 'sharedWithGroup'],
243
-			[IShare::TYPE_LINK, ''],
244
-			[IShare::TYPE_REMOTE, '[email protected]'],
245
-		];
246
-	}
247
-
248
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataTestDelete')]
249
-	public function testDelete($shareType, $sharedWith): void {
250
-		$manager = $this->createManagerMock()
251
-			->onlyMethods(['getShareById', 'deleteChildren', 'promoteReshares'])
252
-			->getMock();
253
-
254
-		$manager->method('deleteChildren')->willReturn([]);
255
-
256
-		$path = $this->createMock(File::class);
257
-		$path->method('getId')->willReturn(1);
258
-
259
-		$share = $this->manager->newShare();
260
-		$share->setId(42)
261
-			->setProviderId('prov')
262
-			->setShareType($shareType)
263
-			->setSharedWith($sharedWith)
264
-			->setSharedBy('sharedBy')
265
-			->setNode($path)
266
-			->setTarget('myTarget');
267
-
268
-		$manager->expects($this->once())->method('deleteChildren')->with($share);
269
-		$manager->expects($this->once())->method('promoteReshares')->with($share);
270
-
271
-		$this->defaultProvider
272
-			->expects($this->once())
273
-			->method('delete')
274
-			->with($share);
275
-
276
-		$calls = [
277
-			BeforeShareDeletedEvent::class,
278
-			ShareDeletedEvent::class,
279
-		];
280
-		$this->dispatcher->expects($this->exactly(2))
281
-			->method('dispatchTyped')
282
-			->willReturnCallback(function ($event) use (&$calls, $share): void {
283
-				$expected = array_shift($calls);
284
-				$this->assertInstanceOf($expected, $event);
285
-				$this->assertEquals($share, $event->getShare());
286
-			});
287
-
288
-		$manager->deleteShare($share);
289
-	}
290
-
291
-	public function testDeleteLazyShare(): void {
292
-		$manager = $this->createManagerMock()
293
-			->onlyMethods(['getShareById', 'deleteChildren', 'promoteReshares'])
294
-			->getMock();
295
-
296
-		$manager->method('deleteChildren')->willReturn([]);
297
-
298
-		$share = $this->manager->newShare();
299
-		$share->setId(42)
300
-			->setProviderId('prov')
301
-			->setShareType(IShare::TYPE_USER)
302
-			->setSharedWith('sharedWith')
303
-			->setSharedBy('sharedBy')
304
-			->setShareOwner('shareOwner')
305
-			->setTarget('myTarget')
306
-			->setNodeId(1)
307
-			->setNodeType('file');
308
-
309
-		$this->rootFolder->expects($this->never())->method($this->anything());
310
-
311
-		$manager->expects($this->once())->method('deleteChildren')->with($share);
312
-		$manager->expects($this->once())->method('promoteReshares')->with($share);
313
-
314
-		$this->defaultProvider
315
-			->expects($this->once())
316
-			->method('delete')
317
-			->with($share);
318
-
319
-		$calls = [
320
-			BeforeShareDeletedEvent::class,
321
-			ShareDeletedEvent::class,
322
-		];
323
-		$this->dispatcher->expects($this->exactly(2))
324
-			->method('dispatchTyped')
325
-			->willReturnCallback(function ($event) use (&$calls, $share): void {
326
-				$expected = array_shift($calls);
327
-				$this->assertInstanceOf($expected, $event);
328
-				$this->assertEquals($share, $event->getShare());
329
-			});
330
-
331
-		$manager->deleteShare($share);
332
-	}
333
-
334
-	public function testDeleteNested(): void {
335
-		$manager = $this->createManagerMock()
336
-			->onlyMethods(['getShareById', 'promoteReshares'])
337
-			->getMock();
338
-
339
-		$path = $this->createMock(File::class);
340
-		$path->method('getId')->willReturn(1);
341
-
342
-		$share1 = $this->manager->newShare();
343
-		$share1->setId(42)
344
-			->setProviderId('prov')
345
-			->setShareType(IShare::TYPE_USER)
346
-			->setSharedWith('sharedWith1')
347
-			->setSharedBy('sharedBy1')
348
-			->setNode($path)
349
-			->setTarget('myTarget1');
350
-
351
-		$share2 = $this->manager->newShare();
352
-		$share2->setId(43)
353
-			->setProviderId('prov')
354
-			->setShareType(IShare::TYPE_GROUP)
355
-			->setSharedWith('sharedWith2')
356
-			->setSharedBy('sharedBy2')
357
-			->setNode($path)
358
-			->setTarget('myTarget2')
359
-			->setParent(42);
360
-
361
-		$share3 = $this->manager->newShare();
362
-		$share3->setId(44)
363
-			->setProviderId('prov')
364
-			->setShareType(IShare::TYPE_LINK)
365
-			->setSharedBy('sharedBy3')
366
-			->setNode($path)
367
-			->setTarget('myTarget3')
368
-			->setParent(43);
369
-
370
-		$this->defaultProvider
371
-			->method('getChildren')
372
-			->willReturnMap([
373
-				[$share1, [$share2]],
374
-				[$share2, [$share3]],
375
-				[$share3, []],
376
-			]);
377
-
378
-		$deleteCalls = [
379
-			$share3,
380
-			$share2,
381
-			$share1,
382
-		];
383
-		$this->defaultProvider->expects($this->exactly(3))
384
-			->method('delete')
385
-			->willReturnCallback(function ($share) use (&$deleteCalls): void {
386
-				$expected = array_shift($deleteCalls);
387
-				$this->assertEquals($expected, $share);
388
-			});
389
-
390
-		$dispatchCalls = [
391
-			[BeforeShareDeletedEvent::class, $share1],
392
-			[BeforeShareDeletedEvent::class, $share2],
393
-			[BeforeShareDeletedEvent::class, $share3],
394
-			[ShareDeletedEvent::class, $share3],
395
-			[ShareDeletedEvent::class, $share2],
396
-			[ShareDeletedEvent::class, $share1],
397
-		];
398
-		$this->dispatcher->expects($this->exactly(6))
399
-			->method('dispatchTyped')
400
-			->willReturnCallback(function ($event) use (&$dispatchCalls): void {
401
-				$expected = array_shift($dispatchCalls);
402
-				$this->assertInstanceOf($expected[0], $event);
403
-				$this->assertEquals($expected[1]->getId(), $event->getShare()->getId());
404
-			});
405
-
406
-		$manager->deleteShare($share1);
407
-	}
408
-
409
-	public function testDeleteFromSelf(): void {
410
-		$manager = $this->createManagerMock()
411
-			->onlyMethods(['getShareById'])
412
-			->getMock();
413
-
414
-		$recipientId = 'unshareFrom';
415
-		$share = $this->manager->newShare();
416
-		$share->setId(42)
417
-			->setProviderId('prov')
418
-			->setShareType(IShare::TYPE_USER)
419
-			->setSharedWith('sharedWith')
420
-			->setSharedBy('sharedBy')
421
-			->setShareOwner('shareOwner')
422
-			->setTarget('myTarget')
423
-			->setNodeId(1)
424
-			->setNodeType('file');
425
-
426
-		$this->defaultProvider
427
-			->expects($this->once())
428
-			->method('deleteFromSelf')
429
-			->with($share, $recipientId);
430
-
431
-		$this->dispatcher->expects($this->once())
432
-			->method('dispatchTyped')
433
-			->with(
434
-				$this->callBack(function (ShareDeletedFromSelfEvent $e) use ($share) {
435
-					return $e->getShare() === $share;
436
-				})
437
-			);
438
-
439
-		$manager->deleteFromSelf($share, $recipientId);
440
-	}
441
-
442
-	public function testDeleteChildren(): void {
443
-		$manager = $this->createManagerMock()
444
-			->onlyMethods(['deleteShare'])
445
-			->getMock();
446
-
447
-		$share = $this->createMock(IShare::class);
448
-		$share->method('getShareType')->willReturn(IShare::TYPE_USER);
449
-
450
-		$child1 = $this->createMock(IShare::class);
451
-		$child1->method('getShareType')->willReturn(IShare::TYPE_USER);
452
-		$child2 = $this->createMock(IShare::class);
453
-		$child2->method('getShareType')->willReturn(IShare::TYPE_USER);
454
-		$child3 = $this->createMock(IShare::class);
455
-		$child3->method('getShareType')->willReturn(IShare::TYPE_USER);
456
-
457
-		$shares = [
458
-			$child1,
459
-			$child2,
460
-			$child3,
461
-		];
462
-
463
-		$this->defaultProvider
464
-			->expects($this->exactly(4))
465
-			->method('getChildren')
466
-			->willReturnCallback(function ($_share) use ($share, $shares) {
467
-				if ($_share === $share) {
468
-					return $shares;
469
-				}
470
-				return [];
471
-			});
472
-
473
-		$calls = [
474
-			$child1,
475
-			$child2,
476
-			$child3,
477
-		];
478
-		$this->defaultProvider->expects($this->exactly(3))
479
-			->method('delete')
480
-			->willReturnCallback(function ($share) use (&$calls): void {
481
-				$expected = array_shift($calls);
482
-				$this->assertEquals($expected, $share);
483
-			});
484
-
485
-		$result = self::invokePrivate($manager, 'deleteChildren', [$share]);
486
-		$this->assertSame($shares, $result);
487
-	}
488
-
489
-	public function testPromoteReshareFile(): void {
490
-		$manager = $this->createManagerMock()
491
-			->onlyMethods(['updateShare', 'getSharesInFolder', 'generalCreateChecks'])
492
-			->getMock();
493
-
494
-		$file = $this->createMock(File::class);
495
-
496
-		$share = $this->createMock(IShare::class);
497
-		$share->method('getShareType')->willReturn(IShare::TYPE_USER);
498
-		$share->method('getNodeType')->willReturn('folder');
499
-		$share->method('getSharedWith')->willReturn('userB');
500
-		$share->method('getNode')->willReturn($file);
501
-
502
-		$reShare = $this->createMock(IShare::class);
503
-		$reShare->method('getShareType')->willReturn(IShare::TYPE_USER);
504
-		$reShare->method('getSharedBy')->willReturn('userB');
505
-		$reShare->method('getSharedWith')->willReturn('userC');
506
-		$reShare->method('getNode')->willReturn($file);
507
-
508
-		$this->defaultProvider->method('getSharesBy')
509
-			->willReturnCallback(function ($userId, $shareType, $node, $reshares, $limit, $offset) use ($reShare, $file) {
510
-				$this->assertEquals($file, $node);
511
-				if ($shareType === IShare::TYPE_USER) {
512
-					return match($userId) {
513
-						'userB' => [$reShare],
514
-					};
515
-				} else {
516
-					return [];
517
-				}
518
-			});
519
-		$manager->method('generalCreateChecks')->willThrowException(new GenericShareException());
520
-
521
-		$manager->expects($this->exactly(1))->method('updateShare')->with($reShare);
522
-
523
-		self::invokePrivate($manager, 'promoteReshares', [$share]);
524
-	}
525
-
526
-	public function testPromoteReshare(): void {
527
-		$manager = $this->createManagerMock()
528
-			->onlyMethods(['updateShare', 'getSharesInFolder', 'generalCreateChecks'])
529
-			->getMock();
530
-
531
-		$folder = $this->createFolderMock('/path/to/folder');
532
-
533
-		$subFolder = $this->createFolderMock('/path/to/folder/sub');
534
-
535
-		$otherFolder = $this->createFolderMock('/path/to/otherfolder/');
536
-
537
-		$share = $this->createMock(IShare::class);
538
-		$share->method('getShareType')->willReturn(IShare::TYPE_USER);
539
-		$share->method('getNodeType')->willReturn('folder');
540
-		$share->method('getSharedWith')->willReturn('userB');
541
-		$share->method('getNode')->willReturn($folder);
542
-
543
-		$reShare = $this->createMock(IShare::class);
544
-		$reShare->method('getShareType')->willReturn(IShare::TYPE_USER);
545
-		$reShare->method('getSharedBy')->willReturn('userB');
546
-		$reShare->method('getSharedWith')->willReturn('userC');
547
-		$reShare->method('getNode')->willReturn($folder);
548
-
549
-		$reShareInSubFolder = $this->createMock(IShare::class);
550
-		$reShareInSubFolder->method('getShareType')->willReturn(IShare::TYPE_USER);
551
-		$reShareInSubFolder->method('getSharedBy')->willReturn('userB');
552
-		$reShareInSubFolder->method('getNode')->willReturn($subFolder);
553
-
554
-		$reShareInOtherFolder = $this->createMock(IShare::class);
555
-		$reShareInOtherFolder->method('getShareType')->willReturn(IShare::TYPE_USER);
556
-		$reShareInOtherFolder->method('getSharedBy')->willReturn('userB');
557
-		$reShareInOtherFolder->method('getNode')->willReturn($otherFolder);
558
-
559
-		$this->defaultProvider->method('getSharesBy')
560
-			->willReturnCallback(function ($userId, $shareType, $node, $reshares, $limit, $offset) use ($reShare, $reShareInSubFolder, $reShareInOtherFolder) {
561
-				if ($shareType === IShare::TYPE_USER) {
562
-					return match($userId) {
563
-						'userB' => [$reShare,$reShareInSubFolder,$reShareInOtherFolder],
564
-					};
565
-				} else {
566
-					return [];
567
-				}
568
-			});
569
-		$manager->method('generalCreateChecks')->willThrowException(new GenericShareException());
570
-
571
-		$calls = [
572
-			$reShare,
573
-			$reShareInSubFolder,
574
-		];
575
-		$manager->expects($this->exactly(2))
576
-			->method('updateShare')
577
-			->willReturnCallback(function ($share) use (&$calls): void {
578
-				$expected = array_shift($calls);
579
-				$this->assertEquals($expected, $share);
580
-			});
581
-
582
-		self::invokePrivate($manager, 'promoteReshares', [$share]);
583
-	}
584
-
585
-	public function testPromoteReshareWhenUserHasAnotherShare(): void {
586
-		$manager = $this->createManagerMock()
587
-			->onlyMethods(['updateShare', 'getSharesInFolder', 'getSharedWith', 'generalCreateChecks'])
588
-			->getMock();
589
-
590
-		$folder = $this->createFolderMock('/path/to/folder');
591
-
592
-		$share = $this->createMock(IShare::class);
593
-		$share->method('getShareType')->willReturn(IShare::TYPE_USER);
594
-		$share->method('getNodeType')->willReturn('folder');
595
-		$share->method('getSharedWith')->willReturn('userB');
596
-		$share->method('getNode')->willReturn($folder);
597
-
598
-		$reShare = $this->createMock(IShare::class);
599
-		$reShare->method('getShareType')->willReturn(IShare::TYPE_USER);
600
-		$reShare->method('getNodeType')->willReturn('folder');
601
-		$reShare->method('getSharedBy')->willReturn('userB');
602
-		$reShare->method('getNode')->willReturn($folder);
603
-
604
-		$this->defaultProvider->method('getSharesBy')->willReturn([$reShare]);
605
-		$manager->method('generalCreateChecks')->willReturn(true);
606
-
607
-		/* No share is promoted because generalCreateChecks does not throw */
608
-		$manager->expects($this->never())->method('updateShare');
609
-
610
-		self::invokePrivate($manager, 'promoteReshares', [$share]);
611
-	}
612
-
613
-	public function testPromoteReshareOfUsersInGroupShare(): void {
614
-		$manager = $this->createManagerMock()
615
-			->onlyMethods(['updateShare', 'getSharesInFolder', 'getSharedWith', 'generalCreateChecks'])
616
-			->getMock();
617
-
618
-		$folder = $this->createFolderMock('/path/to/folder');
619
-
620
-		$userA = $this->createMock(IUser::class);
621
-		$userA->method('getUID')->willReturn('userA');
622
-
623
-		$share = $this->createMock(IShare::class);
624
-		$share->method('getShareType')->willReturn(IShare::TYPE_GROUP);
625
-		$share->method('getNodeType')->willReturn('folder');
626
-		$share->method('getSharedWith')->willReturn('Group');
627
-		$share->method('getNode')->willReturn($folder);
628
-		$share->method('getShareOwner')->willReturn($userA);
629
-
630
-		$reShare1 = $this->createMock(IShare::class);
631
-		$reShare1->method('getShareType')->willReturn(IShare::TYPE_USER);
632
-		$reShare1->method('getNodeType')->willReturn('folder');
633
-		$reShare1->method('getSharedBy')->willReturn('userB');
634
-		$reShare1->method('getNode')->willReturn($folder);
635
-
636
-		$reShare2 = $this->createMock(IShare::class);
637
-		$reShare2->method('getShareType')->willReturn(IShare::TYPE_USER);
638
-		$reShare2->method('getNodeType')->willReturn('folder');
639
-		$reShare2->method('getSharedBy')->willReturn('userC');
640
-		$reShare2->method('getNode')->willReturn($folder);
641
-
642
-		$userB = $this->createMock(IUser::class);
643
-		$userB->method('getUID')->willReturn('userB');
644
-		$userC = $this->createMock(IUser::class);
645
-		$userC->method('getUID')->willReturn('userC');
646
-		$group = $this->createMock(IGroup::class);
647
-		$group->method('getUsers')->willReturn([$userB, $userC]);
648
-		$this->groupManager->method('get')->with('Group')->willReturn($group);
649
-
650
-		$this->defaultProvider->method('getSharesBy')
651
-			->willReturnCallback(function ($userId, $shareType, $node, $reshares, $limit, $offset) use ($reShare1, $reShare2) {
652
-				if ($shareType === IShare::TYPE_USER) {
653
-					return match($userId) {
654
-						'userB' => [$reShare1],
655
-						'userC' => [$reShare2],
656
-					};
657
-				} else {
658
-					return [];
659
-				}
660
-			});
661
-		$manager->method('generalCreateChecks')->willThrowException(new GenericShareException());
662
-
663
-		$manager->method('getSharedWith')->willReturn([]);
664
-
665
-		$calls = [
666
-			$reShare1,
667
-			$reShare2,
668
-		];
669
-		$manager->expects($this->exactly(2))
670
-			->method('updateShare')
671
-			->willReturnCallback(function ($share) use (&$calls): void {
672
-				$expected = array_shift($calls);
673
-				$this->assertEquals($expected, $share);
674
-			});
675
-
676
-		self::invokePrivate($manager, 'promoteReshares', [$share]);
677
-	}
678
-
679
-	public function testGetShareById(): void {
680
-		$share = $this->createMock(IShare::class);
681
-
682
-		$this->defaultProvider
683
-			->expects($this->once())
684
-			->method('getShareById')
685
-			->with(42)
686
-			->willReturn($share);
687
-
688
-		$this->assertEquals($share, $this->manager->getShareById('default:42'));
689
-	}
690
-
691
-
692
-	public function testGetExpiredShareById(): void {
693
-		$this->expectException(ShareNotFound::class);
694
-
695
-		$manager = $this->createManagerMock()
696
-			->onlyMethods(['deleteShare'])
697
-			->getMock();
698
-
699
-		$date = new \DateTime();
700
-		$date->setTime(0, 0, 0);
701
-
702
-		$share = $this->manager->newShare();
703
-		$share->setExpirationDate($date)
704
-			->setShareType(IShare::TYPE_LINK);
705
-
706
-		$this->defaultProvider->expects($this->once())
707
-			->method('getShareById')
708
-			->with('42')
709
-			->willReturn($share);
710
-
711
-		$manager->expects($this->once())
712
-			->method('deleteShare')
713
-			->with($share);
714
-
715
-		$manager->getShareById('default:42');
716
-	}
717
-
718
-
719
-	public function testVerifyPasswordNullButEnforced(): void {
720
-		$this->expectException(\InvalidArgumentException::class);
721
-		$this->expectExceptionMessage('Passwords are enforced for link and mail shares');
722
-
723
-		$this->config->method('getAppValue')->willReturnMap([
724
-			['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
725
-		]);
726
-
727
-		$this->appConfig->method('getValueBool')->willReturnMap([
728
-			['core', 'shareapi_enforce_links_password', true],
729
-		]);
730
-
731
-		self::invokePrivate($this->manager, 'verifyPassword', [null]);
732
-	}
733
-
734
-	public function testVerifyPasswordNotEnforcedGroup(): void {
735
-		$this->config->method('getAppValue')->willReturnMap([
736
-			['core', 'shareapi_enforce_links_password_excluded_groups', '', '["admin"]'],
737
-			['core', 'shareapi_enforce_links_password', 'no', 'yes'],
738
-		]);
739
-
740
-		// Create admin user
741
-		$user = $this->createMock(IUser::class);
742
-		$this->userSession->method('getUser')->willReturn($user);
743
-		$this->groupManager->method('getUserGroupIds')->with($user)->willReturn(['admin']);
744
-
745
-		$result = self::invokePrivate($this->manager, 'verifyPassword', [null]);
746
-		$this->assertNull($result);
747
-	}
748
-
749
-	public function testVerifyPasswordNotEnforcedMultipleGroups(): void {
750
-		$this->config->method('getAppValue')->willReturnMap([
751
-			['core', 'shareapi_enforce_links_password_excluded_groups', '', '["admin", "special"]'],
752
-			['core', 'shareapi_enforce_links_password', 'no', 'yes'],
753
-		]);
754
-
755
-		// Create admin user
756
-		$user = $this->createMock(IUser::class);
757
-		$this->userSession->method('getUser')->willReturn($user);
758
-		$this->groupManager->method('getUserGroupIds')->with($user)->willReturn(['special']);
759
-
760
-		$result = self::invokePrivate($this->manager, 'verifyPassword', [null]);
761
-		$this->assertNull($result);
762
-	}
763
-
764
-	public function testVerifyPasswordNull(): void {
765
-		$this->config->method('getAppValue')->willReturnMap([
766
-			['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
767
-			['core', 'shareapi_enforce_links_password', 'no', 'no'],
768
-		]);
769
-
770
-		$result = self::invokePrivate($this->manager, 'verifyPassword', [null]);
771
-		$this->assertNull($result);
772
-	}
773
-
774
-	public function testVerifyPasswordHook(): void {
775
-		$this->config->method('getAppValue')->willReturnMap([
776
-			['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
777
-			['core', 'shareapi_enforce_links_password', 'no', 'no'],
778
-		]);
779
-
780
-		$this->dispatcher->expects($this->once())->method('dispatchTyped')
781
-			->willReturnCallback(function (Event $event): void {
782
-				$this->assertInstanceOf(ValidatePasswordPolicyEvent::class, $event);
783
-				/** @var ValidatePasswordPolicyEvent $event */
784
-				$this->assertSame('password', $event->getPassword());
785
-			}
786
-			);
787
-
788
-		$result = self::invokePrivate($this->manager, 'verifyPassword', ['password']);
789
-		$this->assertNull($result);
790
-	}
791
-
792
-
793
-	public function testVerifyPasswordHookFails(): void {
794
-		$this->expectException(\Exception::class);
795
-		$this->expectExceptionMessage('password not accepted');
796
-
797
-		$this->config->method('getAppValue')->willReturnMap([
798
-			['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
799
-			['core', 'shareapi_enforce_links_password', 'no', 'no'],
800
-		]);
801
-
802
-		$this->dispatcher->expects($this->once())->method('dispatchTyped')
803
-			->willReturnCallback(function (Event $event): void {
804
-				$this->assertInstanceOf(ValidatePasswordPolicyEvent::class, $event);
805
-				/** @var ValidatePasswordPolicyEvent $event */
806
-				$this->assertSame('password', $event->getPassword());
807
-				throw new HintException('password not accepted');
808
-			}
809
-			);
810
-
811
-		self::invokePrivate($this->manager, 'verifyPassword', ['password']);
812
-	}
813
-
814
-	public function createShare($id, $type, $node, $sharedWith, $sharedBy, $shareOwner,
815
-		$permissions, $expireDate = null, $password = null, $attributes = null) {
816
-		$share = $this->createMock(IShare::class);
817
-
818
-		$share->method('getShareType')->willReturn($type);
819
-		$share->method('getSharedWith')->willReturn($sharedWith);
820
-		$share->method('getSharedBy')->willReturn($sharedBy);
821
-		$share->method('getShareOwner')->willReturn($shareOwner);
822
-		$share->method('getNode')->willReturn($node);
823
-		if ($node && $node->getId()) {
824
-			$share->method('getNodeId')->willReturn($node->getId());
825
-		}
826
-		$share->method('getPermissions')->willReturn($permissions);
827
-		$share->method('getAttributes')->willReturn($attributes);
828
-		$share->method('getExpirationDate')->willReturn($expireDate);
829
-		$share->method('getPassword')->willReturn($password);
830
-
831
-		return $share;
832
-	}
833
-
834
-	public static function dataGeneralChecks(): array {
835
-		$user0 = 'user0';
836
-		$user2 = 'user1';
837
-		$group0 = 'group0';
838
-
839
-		$file = [
840
-			File::class,
841
-			[
842
-				'getId' => 108,
843
-			],
844
-			'default',
845
-		];
846
-
847
-		$node = [
848
-			Node::class,
849
-			[
850
-				'getId' => 108,
851
-			],
852
-			'default',
853
-		];
854
-
855
-		$data = [
856
-			[[null, IShare::TYPE_USER, $file, null, $user0, $user0, 31, null, null], 'Share recipient is not a valid user', true],
857
-			[[null, IShare::TYPE_USER, $file, $group0, $user0, $user0, 31, null, null], 'Share recipient is not a valid user', true],
858
-			[[null, IShare::TYPE_USER, $file, '[email protected]', $user0, $user0, 31, null, null], 'Share recipient is not a valid user', true],
859
-			[[null, IShare::TYPE_GROUP, $file, null, $user0, $user0, 31, null, null], 'Share recipient is not a valid group', true],
860
-			[[null, IShare::TYPE_GROUP, $file, $user2, $user0, $user0, 31, null, null], 'Share recipient is not a valid group', true],
861
-			[[null, IShare::TYPE_GROUP, $file, '[email protected]', $user0, $user0, 31, null, null], 'Share recipient is not a valid group', true],
862
-			[[null, IShare::TYPE_LINK, $file, $user2, $user0, $user0, 31, null, null], 'Share recipient should be empty', true],
863
-			[[null, IShare::TYPE_LINK, $file, $group0, $user0, $user0, 31, null, null], 'Share recipient should be empty', true],
864
-			[[null, IShare::TYPE_LINK, $file, '[email protected]', $user0, $user0, 31, null, null], 'Share recipient should be empty', true],
865
-			[[null, -1, $file, null, $user0, $user0, 31, null, null], 'Unknown share type', true],
866
-
867
-			[[null, IShare::TYPE_USER, $file, $user2, null, $user0, 31, null, null], 'Share initiator must be set', true],
868
-			[[null, IShare::TYPE_GROUP, $file, $group0, null, $user0, 31, null, null], 'Share initiator must be set', true],
869
-			[[null, IShare::TYPE_LINK, $file, null, null, $user0, 31, null, null], 'Share initiator must be set', true],
870
-
871
-			[[null, IShare::TYPE_USER, $file, $user0, $user0, $user0, 31, null, null], 'Cannot share with yourself', true],
872
-
873
-			[[null, IShare::TYPE_USER, null, $user2, $user0, $user0, 31, null, null], 'Shared path must be set', true],
874
-			[[null, IShare::TYPE_GROUP, null, $group0, $user0, $user0, 31, null, null], 'Shared path must be set', true],
875
-			[[null, IShare::TYPE_LINK, null, null, $user0, $user0, 31, null, null], 'Shared path must be set', true],
876
-
877
-			[[null, IShare::TYPE_USER, $node, $user2, $user0, $user0, 31, null, null], 'Shared path must be either a file or a folder', true],
878
-			[[null, IShare::TYPE_GROUP, $node, $group0, $user0, $user0, 31, null, null], 'Shared path must be either a file or a folder', true],
879
-			[[null, IShare::TYPE_LINK, $node, null, $user0, $user0, 31, null, null], 'Shared path must be either a file or a folder', true],
880
-		];
881
-
882
-		$nonShareAble = [
883
-			Folder::class,
884
-			[
885
-				'getId' => 108,
886
-				'isShareable' => false,
887
-				'getPath' => 'path',
888
-				'getName' => 'name',
889
-				'getOwner' => $user0,
890
-			],
891
-			'default',
892
-		];
893
-
894
-		$data[] = [[null, IShare::TYPE_USER, $nonShareAble, $user2, $user0, $user0, 31, null, null], 'You are not allowed to share name', true];
895
-		$data[] = [[null, IShare::TYPE_GROUP, $nonShareAble, $group0, $user0, $user0, 31, null, null], 'You are not allowed to share name', true];
896
-		$data[] = [[null, IShare::TYPE_LINK, $nonShareAble, null, $user0, $user0, 31, null, null], 'You are not allowed to share name', true];
897
-
898
-		$limitedPermssions = [
899
-			File::class,
900
-			[
901
-				'isShareable' => true,
902
-				'getPermissions' => Constants::PERMISSION_READ,
903
-				'getId' => 108,
904
-				'getPath' => 'path',
905
-				'getName' => 'name',
906
-				'getOwner' => $user0,
907
-			],
908
-			'default',
909
-		];
910
-
911
-		$data[] = [[null, IShare::TYPE_USER, $limitedPermssions, $user2, $user0, $user0, null, null, null], 'Valid permissions are required for sharing', true];
912
-		$data[] = [[null, IShare::TYPE_GROUP, $limitedPermssions, $group0, $user0, $user0, null, null, null], 'Valid permissions are required for sharing', true];
913
-		$data[] = [[null, IShare::TYPE_LINK, $limitedPermssions, null, $user0, $user0, null, null, null], 'Valid permissions are required for sharing', true];
914
-
915
-		$limitedPermssions[1]['getMountPoint'] = MoveableMount::class;
916
-
917
-		// increase permissions of a re-share
918
-		$data[] = [[null, IShare::TYPE_GROUP, $limitedPermssions, $group0, $user0, $user0, 17, null, null], 'Cannot increase permissions of path', true];
919
-		$data[] = [[null, IShare::TYPE_USER, $limitedPermssions, $user2, $user0, $user0, 3, null, null], 'Cannot increase permissions of path', true];
920
-
921
-		$nonMoveableMountPermssions = [
922
-			Folder::class,
923
-			[
924
-				'isShareable' => true,
925
-				'getPermissions' => Constants::PERMISSION_READ,
926
-				'getId' => 108,
927
-				'getPath' => 'path',
928
-				'getName' => 'name',
929
-				'getInternalPath' => '',
930
-				'getOwner' => $user0,
931
-			],
932
-			'allPermssions',
933
-		];
934
-
935
-		$data[] = [[null, IShare::TYPE_USER, $nonMoveableMountPermssions, $user2, $user0, $user0, 11, null, null], 'Cannot increase permissions of path', false];
936
-		$data[] = [[null, IShare::TYPE_GROUP, $nonMoveableMountPermssions, $group0, $user0, $user0, 11, null, null], 'Cannot increase permissions of path', false];
937
-
938
-		$rootFolder = [
939
-			Folder::class,
940
-			[
941
-				'isShareable' => true,
942
-				'getPermissions' => Constants::PERMISSION_ALL,
943
-				'getId' => 42,
944
-			],
945
-			'none',
946
-		];
947
-
948
-		$data[] = [[null, IShare::TYPE_USER, $rootFolder, $user2, $user0, $user0, 30, null, null], 'You cannot share your root folder', true];
949
-		$data[] = [[null, IShare::TYPE_GROUP, $rootFolder, $group0, $user0, $user0, 2, null, null], 'You cannot share your root folder', true];
950
-		$data[] = [[null, IShare::TYPE_LINK, $rootFolder, null, $user0, $user0, 16, null, null], 'You cannot share your root folder', true];
951
-
952
-		$allPermssionsFiles = [
953
-			File::class,
954
-			[
955
-				'isShareable' => true,
956
-				'getPermissions' => Constants::PERMISSION_ALL,
957
-				'getId' => 187,
958
-				'getOwner' => $user0,
959
-			],
960
-			'default',
961
-		];
962
-
963
-		// test invalid CREATE or DELETE permissions
964
-		$data[] = [[null, IShare::TYPE_USER, $allPermssionsFiles, $user2, $user0, $user0, Constants::PERMISSION_ALL, null, null], 'File shares cannot have create or delete permissions', true];
965
-		$data[] = [[null, IShare::TYPE_GROUP, $allPermssionsFiles, $group0, $user0, $user0, Constants::PERMISSION_READ | Constants::PERMISSION_CREATE, null, null], 'File shares cannot have create or delete permissions', true];
966
-		$data[] = [[null, IShare::TYPE_LINK, $allPermssionsFiles, null, $user0, $user0, Constants::PERMISSION_READ | Constants::PERMISSION_DELETE, null, null], 'File shares cannot have create or delete permissions', true];
967
-
968
-		$allPermssions = [
969
-			Folder::class,
970
-			[
971
-				'isShareable' => true,
972
-				'getPermissions' => Constants::PERMISSION_ALL,
973
-				'getId' => 108,
974
-				'getOwner' => $user0,
975
-			],
976
-			'default',
977
-		];
978
-
979
-		$data[] = [[null, IShare::TYPE_USER, $allPermssions, $user2, $user0, $user0, 30, null, null], 'Shares need at least read permissions', true];
980
-		$data[] = [[null, IShare::TYPE_GROUP, $allPermssions, $group0, $user0, $user0, 2, null, null], 'Shares need at least read permissions', true];
981
-
982
-		// test invalid permissions
983
-		$data[] = [[null, IShare::TYPE_USER, $allPermssions, $user2, $user0, $user0, 32, null, null], 'Valid permissions are required for sharing', true];
984
-		$data[] = [[null, IShare::TYPE_GROUP, $allPermssions, $group0, $user0, $user0, 63, null, null], 'Valid permissions are required for sharing', true];
985
-		$data[] = [[null, IShare::TYPE_LINK, $allPermssions, null, $user0, $user0, -1, null, null], 'Valid permissions are required for sharing', true];
986
-
987
-		// working shares
988
-		$data[] = [[null, IShare::TYPE_USER, $allPermssions, $user2, $user0, $user0, 31, null, null], null, false];
989
-		$data[] = [[null, IShare::TYPE_GROUP, $allPermssions, $group0, $user0, $user0, 3, null, null], null, false];
990
-		$data[] = [[null, IShare::TYPE_LINK, $allPermssions, null, $user0, $user0, 17, null, null], null, false];
991
-
992
-		$remoteFile = [
993
-			Folder::class,
994
-			[
995
-				'isShareable' => true,
996
-				'getPermissions' => Constants::PERMISSION_READ ^ Constants::PERMISSION_UPDATE,
997
-				'getId' => 108,
998
-				'getOwner' => $user0,
999
-			],
1000
-			'remote',
1001
-		];
1002
-
1003
-		$data[] = [[null, IShare::TYPE_REMOTE, $remoteFile, $user2, $user0, $user0, 1, null, null], null, false];
1004
-		$data[] = [[null, IShare::TYPE_REMOTE, $remoteFile, $user2, $user0, $user0, 3, null, null], null, false];
1005
-		$data[] = [[null, IShare::TYPE_REMOTE, $remoteFile, $user2, $user0, $user0, 31, null, null], 'Cannot increase permissions of ', true];
1006
-
1007
-		return $data;
1008
-	}
1009
-
1010
-	private function createNodeMock(string $class, array $methods, string $storageType): MockObject {
1011
-		$mock = $this->createMock($class);
1012
-		foreach ($methods as $methodName => $return) {
1013
-			if ($methodName === 'getOwner') {
1014
-				$uid = $return;
1015
-				$return = $this->createMock(IUser::class);
1016
-				$return->method('getUID')
1017
-					->willReturn($uid);
1018
-			} elseif ($methodName === 'getMountPoint') {
1019
-				$return = $this->createMock($return);
1020
-			}
1021
-			$mock->method($methodName)->willReturn($return);
1022
-		}
1023
-		switch ($storageType) {
1024
-			case 'default':
1025
-				$storage = $this->createMock(IStorage::class);
1026
-				$storage->method('instanceOfStorage')
1027
-					->with('\OCA\Files_Sharing\External\Storage')
1028
-					->willReturn(false);
1029
-				break;
1030
-			case 'allPermssions':
1031
-				$storage = $this->createMock(IStorage::class);
1032
-				$storage->method('instanceOfStorage')
1033
-					->with('\OCA\Files_Sharing\External\Storage')
1034
-					->willReturn(false);
1035
-				$storage->method('getPermissions')->willReturn(Constants::PERMISSION_ALL);
1036
-				break;
1037
-			case 'none':
1038
-				$storage = false;
1039
-				break;
1040
-			case 'remote':
1041
-				$storage = $this->createMock(IStorage::class);
1042
-				$storage->method('instanceOfStorage')
1043
-					->with('\OCA\Files_Sharing\External\Storage')
1044
-					->willReturn(true);
1045
-				break;
1046
-			default:
1047
-				throw new \Exception('Unknown storage type ' . $storageType);
1048
-		}
1049
-		if ($storage === false) {
1050
-			$mock->expects(self::never())->method('getStorage');
1051
-		} else {
1052
-			$mock->method('getStorage')
1053
-				->willReturn($storage);
1054
-		}
1055
-
1056
-		return $mock;
1057
-	}
1058
-
1059
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataGeneralChecks')]
1060
-	public function testGeneralChecks(array $shareParams, ?string $exceptionMessage, bool $exception): void {
1061
-		if ($shareParams[2] !== null) {
1062
-			$shareParams[2] = $this->createNodeMock(...$shareParams[2]);
1063
-		}
1064
-		$share = $this->createShare(...$shareParams);
1065
-
1066
-		$thrown = null;
1067
-
1068
-		$this->userManager->method('userExists')->willReturnMap([
1069
-			['user0', true],
1070
-			['user1', true],
1071
-		]);
1072
-
1073
-		$this->groupManager->method('groupExists')->willReturnMap([
1074
-			['group0', true],
1075
-		]);
1076
-
1077
-		$userFolder = $this->createMock(Folder::class);
1078
-		$userFolder->expects($this->any())
1079
-			->method('getId')
1080
-			->willReturn(42);
1081
-		// Id 108 is used in the data to refer to the node of the share.
1082
-		$userFolder->method('getById')
1083
-			->with(108)
1084
-			->willReturn([$share->getNode()]);
1085
-		$userFolder->expects($this->any())
1086
-			->method('getRelativePath')
1087
-			->willReturnArgument(0);
1088
-		$this->rootFolder->method('getUserFolder')->willReturn($userFolder);
1089
-
1090
-
1091
-		try {
1092
-			self::invokePrivate($this->manager, 'generalCreateChecks', [$share]);
1093
-			$thrown = false;
1094
-		} catch (GenericShareException $e) {
1095
-			$this->assertEquals($exceptionMessage, $e->getHint());
1096
-			$thrown = true;
1097
-		} catch (\InvalidArgumentException $e) {
1098
-			$this->assertEquals($exceptionMessage, $e->getMessage());
1099
-			$thrown = true;
1100
-		}
1101
-
1102
-		$this->assertSame($exception, $thrown);
1103
-	}
1104
-
1105
-
1106
-	public function testGeneralCheckShareRoot(): void {
1107
-		$this->expectException(\InvalidArgumentException::class);
1108
-		$this->expectExceptionMessage('You cannot share your root folder');
1109
-
1110
-		$thrown = null;
1111
-
1112
-		$this->userManager->method('userExists')->willReturnMap([
1113
-			['user0', true],
1114
-			['user1', true],
1115
-		]);
1116
-
1117
-		$userFolder = $this->createMock(Folder::class);
1118
-		$userFolder->method('isSubNode')->with($userFolder)->willReturn(false);
1119
-		$this->rootFolder->method('getUserFolder')->willReturn($userFolder);
1120
-
1121
-		$share = $this->manager->newShare();
1122
-
1123
-		$share->setShareType(IShare::TYPE_USER)
1124
-			->setSharedWith('user0')
1125
-			->setSharedBy('user1')
1126
-			->setNode($userFolder);
1127
-
1128
-		self::invokePrivate($this->manager, 'generalCreateChecks', [$share]);
1129
-	}
1130
-
1131
-	public static function validateExpirationDateInternalProvider() {
1132
-		return [[IShare::TYPE_USER], [IShare::TYPE_REMOTE], [IShare::TYPE_REMOTE_GROUP]];
1133
-	}
1134
-
1135
-	#[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1136
-	public function testValidateExpirationDateInternalInPast($shareType): void {
1137
-		$this->expectException(GenericShareException::class);
1138
-		$this->expectExceptionMessage('Expiration date is in the past');
1139
-
1140
-		// Expire date in the past
1141
-		$past = new \DateTime();
1142
-		$past->sub(new \DateInterval('P1D'));
1143
-
1144
-		$share = $this->manager->newShare();
1145
-		$share->setShareType($shareType);
1146
-		$share->setExpirationDate($past);
1147
-
1148
-		self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1149
-	}
1150
-
1151
-	#[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1152
-	public function testValidateExpirationDateInternalEnforceButNotSet($shareType): void {
1153
-		$this->expectException(\InvalidArgumentException::class);
1154
-		$this->expectExceptionMessage('Expiration date is enforced');
1155
-
1156
-		$share = $this->manager->newShare();
1157
-		$share->setProviderId('foo')->setId('bar');
1158
-		$share->setShareType($shareType);
1159
-		if ($shareType === IShare::TYPE_USER) {
1160
-			$this->config->method('getAppValue')
1161
-				->willReturnMap([
1162
-					['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
1163
-					['core', 'shareapi_enforce_internal_expire_date', 'no', 'yes'],
1164
-				]);
1165
-		} else {
1166
-			$this->config->method('getAppValue')
1167
-				->willReturnMap([
1168
-					['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
1169
-					['core', 'shareapi_enforce_remote_expire_date', 'no', 'yes'],
1170
-				]);
1171
-		}
1172
-
1173
-		self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1174
-	}
1175
-
1176
-	#[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1177
-	public function testValidateExpirationDateInternalEnforceButNotEnabledAndNotSet($shareType): void {
1178
-		$share = $this->manager->newShare();
1179
-		$share->setProviderId('foo')->setId('bar');
1180
-		$share->setShareType($shareType);
1181
-
1182
-		if ($shareType === IShare::TYPE_USER) {
1183
-			$this->config->method('getAppValue')
1184
-				->willReturnMap([
1185
-					['core', 'shareapi_enforce_internal_expire_date', 'no', 'yes'],
1186
-				]);
1187
-		} else {
1188
-			$this->config->method('getAppValue')
1189
-				->willReturnMap([
1190
-					['core', 'shareapi_enforce_remote_expire_date', 'no', 'yes'],
1191
-				]);
1192
-		}
1193
-
1194
-		self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1195
-
1196
-		$this->assertNull($share->getExpirationDate());
1197
-	}
1198
-
1199
-	#[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1200
-	public function testValidateExpirationDateInternalEnforceButNotSetNewShare($shareType): void {
1201
-		$share = $this->manager->newShare();
1202
-		$share->setShareType($shareType);
1203
-
1204
-		if ($shareType === IShare::TYPE_USER) {
1205
-			$this->config->method('getAppValue')
1206
-				->willReturnMap([
1207
-					['core', 'shareapi_enforce_internal_expire_date', 'no', 'yes'],
1208
-					['core', 'shareapi_internal_expire_after_n_days', '7', '3'],
1209
-					['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
1210
-					['core', 'internal_defaultExpDays', '3', '3'],
1211
-				]);
1212
-		} else {
1213
-			$this->config->method('getAppValue')
1214
-				->willReturnMap([
1215
-					['core', 'shareapi_enforce_remote_expire_date', 'no', 'yes'],
1216
-					['core', 'shareapi_remote_expire_after_n_days', '7', '3'],
1217
-					['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
1218
-					['core', 'remote_defaultExpDays', '3', '3'],
1219
-				]);
1220
-		}
1221
-
1222
-		$expected = new \DateTime('now', $this->timezone);
1223
-		$expected->setTime(0, 0, 0);
1224
-		$expected->add(new \DateInterval('P3D'));
1225
-
1226
-		self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1227
-
1228
-		$this->assertNotNull($share->getExpirationDate());
1229
-		$this->assertEquals($expected, $share->getExpirationDate());
1230
-	}
1231
-
1232
-	#[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1233
-	public function testValidateExpirationDateInternalEnforceRelaxedDefaultButNotSetNewShare($shareType): void {
1234
-		$share = $this->manager->newShare();
1235
-		$share->setShareType($shareType);
1236
-
1237
-		if ($shareType === IShare::TYPE_USER) {
1238
-			$this->config->method('getAppValue')
1239
-				->willReturnMap([
1240
-					['core', 'shareapi_enforce_internal_expire_date', 'no', 'yes'],
1241
-					['core', 'shareapi_internal_expire_after_n_days', '7', '3'],
1242
-					['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
1243
-					['core', 'internal_defaultExpDays', '3', '1'],
1244
-				]);
1245
-		} else {
1246
-			$this->config->method('getAppValue')
1247
-				->willReturnMap([
1248
-					['core', 'shareapi_enforce_remote_expire_date', 'no', 'yes'],
1249
-					['core', 'shareapi_remote_expire_after_n_days', '7', '3'],
1250
-					['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
1251
-					['core', 'remote_defaultExpDays', '3', '1'],
1252
-				]);
1253
-		}
1254
-
1255
-		$expected = new \DateTime('now', $this->timezone);
1256
-		$expected->setTime(0, 0, 0);
1257
-		$expected->add(new \DateInterval('P1D'));
1258
-
1259
-		self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1260
-
1261
-		$this->assertNotNull($share->getExpirationDate());
1262
-		$this->assertEquals($expected, $share->getExpirationDate());
1263
-	}
1264
-
1265
-	#[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1266
-	public function testValidateExpirationDateInternalEnforceTooFarIntoFuture($shareType): void {
1267
-		$this->expectException(GenericShareException::class);
1268
-		$this->expectExceptionMessage('Cannot set expiration date more than 3 days in the future');
1269
-
1270
-		$future = new \DateTime();
1271
-		$future->add(new \DateInterval('P7D'));
1272
-
1273
-		$share = $this->manager->newShare();
1274
-		$share->setShareType($shareType);
1275
-		$share->setExpirationDate($future);
1276
-
1277
-		if ($shareType === IShare::TYPE_USER) {
1278
-			$this->config->method('getAppValue')
1279
-				->willReturnMap([
1280
-					['core', 'shareapi_enforce_internal_expire_date', 'no', 'yes'],
1281
-					['core', 'shareapi_internal_expire_after_n_days', '7', '3'],
1282
-					['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
1283
-				]);
1284
-		} else {
1285
-			$this->config->method('getAppValue')
1286
-				->willReturnMap([
1287
-					['core', 'shareapi_enforce_remote_expire_date', 'no', 'yes'],
1288
-					['core', 'shareapi_remote_expire_after_n_days', '7', '3'],
1289
-					['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
1290
-				]);
1291
-		}
1292
-
1293
-		self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1294
-	}
1295
-
1296
-	#[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1297
-	public function testValidateExpirationDateInternalEnforceValid($shareType): void {
1298
-		$future = new \DateTime('now', $this->dateTimeZone->getTimeZone());
1299
-		$future->add(new \DateInterval('P2D'));
1300
-		$future->setTime(1, 2, 3);
1301
-
1302
-		$expected = clone $future;
1303
-		$expected->setTime(0, 0, 0);
1304
-
1305
-		$share = $this->manager->newShare();
1306
-		$share->setShareType($shareType);
1307
-		$share->setExpirationDate($future);
1308
-
1309
-		if ($shareType === IShare::TYPE_USER) {
1310
-			$this->config->method('getAppValue')
1311
-				->willReturnMap([
1312
-					['core', 'shareapi_enforce_internal_expire_date', 'no', 'yes'],
1313
-					['core', 'shareapi_internal_expire_after_n_days', '7', '3'],
1314
-					['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
1315
-				]);
1316
-		} else {
1317
-			$this->config->method('getAppValue')
1318
-				->willReturnMap([
1319
-					['core', 'shareapi_enforce_remote_expire_date', 'no', 'yes'],
1320
-					['core', 'shareapi_remote_expire_after_n_days', '7', '3'],
1321
-					['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
1322
-				]);
1323
-		}
1324
-
1325
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
1326
-		Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1327
-		$hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($future) {
1328
-			return $data['expirationDate'] == $future;
1329
-		}));
1330
-
1331
-		self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1332
-
1333
-		$this->assertEquals($expected, $share->getExpirationDate());
1334
-	}
1335
-
1336
-	#[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1337
-	public function testValidateExpirationDateInternalNoDefault($shareType): void {
1338
-		$date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
1339
-		$date->add(new \DateInterval('P5D'));
1340
-		$date->setTime(1, 2, 3);
1341
-
1342
-		$expected = clone $date;
1343
-		$expected->setTime(0, 0, 0);
1344
-
1345
-		$share = $this->manager->newShare();
1346
-		$share->setShareType($shareType);
1347
-		$share->setExpirationDate($date);
1348
-
1349
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
1350
-		Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1351
-		$hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
1352
-			return $data['expirationDate'] == $expected && $data['passwordSet'] === false;
1353
-		}));
1354
-
1355
-		self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1356
-
1357
-		$this->assertEquals($expected, $share->getExpirationDate());
1358
-	}
1359
-
1360
-	#[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1361
-	public function testValidateExpirationDateInternalNoDateNoDefault($shareType): void {
1362
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
1363
-		Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1364
-		$hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) {
1365
-			return $data['expirationDate'] === null && $data['passwordSet'] === true;
1366
-		}));
1367
-
1368
-		$share = $this->manager->newShare();
1369
-		$share->setShareType($shareType);
1370
-		$share->setPassword('password');
1371
-
1372
-		self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1373
-
1374
-		$this->assertNull($share->getExpirationDate());
1375
-	}
1376
-
1377
-	#[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1378
-	public function testValidateExpirationDateInternalNoDateDefault($shareType): void {
1379
-		$share = $this->manager->newShare();
1380
-		$share->setShareType($shareType);
1381
-
1382
-		$expected = new \DateTime('now', $this->timezone);
1383
-		$expected->setTime(0, 0);
1384
-		$expected->add(new \DateInterval('P3D'));
1385
-		$expected->setTimezone(new \DateTimeZone(date_default_timezone_get()));
1386
-
1387
-		if ($shareType === IShare::TYPE_USER) {
1388
-			$this->config->method('getAppValue')
1389
-				->willReturnMap([
1390
-					['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
1391
-					['core', 'shareapi_internal_expire_after_n_days', '7', '3'],
1392
-					['core', 'internal_defaultExpDays', '3', '3'],
1393
-				]);
1394
-		} else {
1395
-			$this->config->method('getAppValue')
1396
-				->willReturnMap([
1397
-					['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
1398
-					['core', 'shareapi_remote_expire_after_n_days', '7', '3'],
1399
-					['core', 'remote_defaultExpDays', '3', '3'],
1400
-				]);
1401
-		}
1402
-
1403
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
1404
-		Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1405
-		$hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
1406
-			return $data['expirationDate'] == $expected;
1407
-		}));
1408
-
1409
-		self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1410
-
1411
-		$this->assertEquals($expected, $share->getExpirationDate());
1412
-	}
1413
-
1414
-	#[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1415
-	public function testValidateExpirationDateInternalDefault($shareType): void {
1416
-		$future = new \DateTime('now', $this->timezone);
1417
-		$future->add(new \DateInterval('P5D'));
1418
-		$future->setTime(1, 2, 3);
1419
-
1420
-		$expected = clone $future;
1421
-		$expected->setTime(0, 0);
1422
-
1423
-		$share = $this->manager->newShare();
1424
-		$share->setShareType($shareType);
1425
-		$share->setExpirationDate($future);
1426
-
1427
-		if ($shareType === IShare::TYPE_USER) {
1428
-			$this->config->method('getAppValue')
1429
-				->willReturnMap([
1430
-					['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
1431
-					['core', 'shareapi_internal_expire_after_n_days', '7', '3'],
1432
-					['core', 'internal_defaultExpDays', '3', '1'],
1433
-				]);
1434
-		} else {
1435
-			$this->config->method('getAppValue')
1436
-				->willReturnMap([
1437
-					['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
1438
-					['core', 'shareapi_remote_expire_after_n_days', '7', '3'],
1439
-					['core', 'remote_defaultExpDays', '3', '1'],
1440
-				]);
1441
-		}
1442
-
1443
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
1444
-		Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1445
-		$hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
1446
-			return $data['expirationDate'] == $expected;
1447
-		}));
1448
-
1449
-		self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1450
-
1451
-		$this->assertEquals($expected, $share->getExpirationDate());
1452
-	}
1453
-
1454
-	#[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1455
-	public function testValidateExpirationDateInternalHookModification($shareType): void {
1456
-		$nextWeek = new \DateTime('now', $this->timezone);
1457
-		$nextWeek->add(new \DateInterval('P7D'));
1458
-		$nextWeek->setTime(0, 0, 0);
1459
-
1460
-		$save = clone $nextWeek;
1461
-
1462
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
1463
-		Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1464
-		$hookListener->expects($this->once())->method('listener')->willReturnCallback(function ($data): void {
1465
-			$data['expirationDate']->sub(new \DateInterval('P2D'));
1466
-		});
1467
-
1468
-		$share = $this->manager->newShare();
1469
-		$share->setShareType($shareType);
1470
-		$share->setExpirationDate($nextWeek);
1471
-
1472
-		self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1473
-
1474
-		$save->sub(new \DateInterval('P2D'));
1475
-		$this->assertEquals($save, $share->getExpirationDate());
1476
-	}
1477
-
1478
-	#[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1479
-	public function testValidateExpirationDateInternalHookException($shareType): void {
1480
-		$this->expectException(\Exception::class);
1481
-		$this->expectExceptionMessage('Invalid date!');
1482
-
1483
-		$nextWeek = new \DateTime();
1484
-		$nextWeek->add(new \DateInterval('P7D'));
1485
-		$nextWeek->setTime(0, 0, 0);
1486
-
1487
-		$share = $this->manager->newShare();
1488
-		$share->setShareType($shareType);
1489
-		$share->setExpirationDate($nextWeek);
1490
-
1491
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
1492
-		Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1493
-		$hookListener->expects($this->once())->method('listener')->willReturnCallback(function ($data): void {
1494
-			$data['accepted'] = false;
1495
-			$data['message'] = 'Invalid date!';
1496
-		});
1497
-
1498
-		self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1499
-	}
1500
-
1501
-	#[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1502
-	public function testValidateExpirationDateInternalExistingShareNoDefault($shareType): void {
1503
-		$share = $this->manager->newShare();
1504
-		$share->setShareType($shareType);
1505
-		$share->setId('42')->setProviderId('foo');
1506
-
1507
-		if ($shareType === IShare::TYPE_USER) {
1508
-			$this->config->method('getAppValue')
1509
-				->willReturnMap([
1510
-					['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
1511
-					['core', 'shareapi_internal_expire_after_n_days', '7', '6'],
1512
-				]);
1513
-		} else {
1514
-			$this->config->method('getAppValue')
1515
-				->willReturnMap([
1516
-					['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
1517
-					['core', 'shareapi_remote_expire_after_n_days', '7', '6'],
1518
-				]);
1519
-		}
1520
-
1521
-		self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1522
-
1523
-		$this->assertEquals(null, $share->getExpirationDate());
1524
-	}
1525
-
1526
-	public function testValidateExpirationDateInPast(): void {
1527
-		$this->expectException(GenericShareException::class);
1528
-		$this->expectExceptionMessage('Expiration date is in the past');
1529
-
1530
-		// Expire date in the past
1531
-		$past = new \DateTime();
1532
-		$past->sub(new \DateInterval('P1D'));
1533
-
1534
-		$share = $this->manager->newShare();
1535
-		$share->setExpirationDate($past);
1536
-
1537
-		self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1538
-	}
1539
-
1540
-	public function testValidateExpirationDateEnforceButNotSet(): void {
1541
-		$this->expectException(\InvalidArgumentException::class);
1542
-		$this->expectExceptionMessage('Expiration date is enforced');
1543
-
1544
-		$share = $this->manager->newShare();
1545
-		$share->setProviderId('foo')->setId('bar');
1546
-
1547
-		$this->appConfig->method('getValueBool')
1548
-			->willReturnMap([
1549
-				['core', 'shareapi_default_expire_date', true],
1550
-				['core', 'shareapi_enforce_expire_date', true],
1551
-			]);
1552
-
1553
-		self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1554
-	}
1555
-
1556
-	public function testValidateExpirationDateEnforceButNotEnabledAndNotSet(): void {
1557
-		$share = $this->manager->newShare();
1558
-		$share->setProviderId('foo')->setId('bar');
1559
-
1560
-		$this->config->method('getAppValue')
1561
-			->willReturnMap([
1562
-				['core', 'shareapi_enforce_expire_date', 'no', 'yes'],
1563
-			]);
1564
-
1565
-		self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1566
-
1567
-		$this->assertNull($share->getExpirationDate());
1568
-	}
1569
-
1570
-	public function testValidateExpirationDateEnforceButNotSetNewShare(): void {
1571
-		$share = $this->manager->newShare();
1572
-
1573
-		$this->config->method('getAppValue')
1574
-			->willReturnMap([
1575
-				['core', 'shareapi_expire_after_n_days', '7', '3'],
1576
-				['core', 'link_defaultExpDays', '3', '3'],
1577
-			]);
1578
-
1579
-		$this->appConfig->method('getValueBool')
1580
-			->willReturnMap([
1581
-				['core', 'shareapi_default_expire_date', true],
1582
-				['core', 'shareapi_enforce_expire_date', true],
1583
-			]);
1584
-
1585
-		$expected = new \DateTime('now', $this->timezone);
1586
-		$expected->setTime(0, 0, 0);
1587
-		$expected->add(new \DateInterval('P3D'));
1588
-
1589
-		self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1590
-
1591
-		$this->assertNotNull($share->getExpirationDate());
1592
-		$this->assertEquals($expected, $share->getExpirationDate());
1593
-	}
1594
-
1595
-	public function testValidateExpirationDateEnforceRelaxedDefaultButNotSetNewShare(): void {
1596
-		$share = $this->manager->newShare();
1597
-
1598
-		$this->config->method('getAppValue')
1599
-			->willReturnMap([
1600
-				['core', 'shareapi_expire_after_n_days', '7', '3'],
1601
-				['core', 'link_defaultExpDays', '3', '1'],
1602
-			]);
1603
-
1604
-		$this->appConfig->method('getValueBool')
1605
-			->willReturnMap([
1606
-				['core', 'shareapi_default_expire_date', true],
1607
-				['core', 'shareapi_enforce_expire_date', true],
1608
-			]);
1609
-
1610
-		$expected = new \DateTime('now', $this->timezone);
1611
-		$expected->setTime(0, 0, 0);
1612
-		$expected->add(new \DateInterval('P1D'));
80
+    /** @var Manager */
81
+    protected $manager;
82
+    /** @var LoggerInterface|MockObject */
83
+    protected $logger;
84
+    /** @var IConfig|MockObject */
85
+    protected $config;
86
+    /** @var ISecureRandom|MockObject */
87
+    protected $secureRandom;
88
+    /** @var IHasher|MockObject */
89
+    protected $hasher;
90
+    /** @var IShareProvider|MockObject */
91
+    protected $defaultProvider;
92
+    /** @var IMountManager|MockObject */
93
+    protected $mountManager;
94
+    /** @var IGroupManager|MockObject */
95
+    protected $groupManager;
96
+    /** @var IL10N|MockObject */
97
+    protected $l;
98
+    /** @var IFactory|MockObject */
99
+    protected $l10nFactory;
100
+    /** @var DummyFactory */
101
+    protected $factory;
102
+    /** @var IUserManager|MockObject */
103
+    protected $userManager;
104
+    /** @var IRootFolder | MockObject */
105
+    protected $rootFolder;
106
+    /** @var IEventDispatcher|MockObject */
107
+    protected $dispatcher;
108
+    /** @var IMailer|MockObject */
109
+    protected $mailer;
110
+    /** @var IURLGenerator|MockObject */
111
+    protected $urlGenerator;
112
+    /** @var \OC_Defaults|MockObject */
113
+    protected $defaults;
114
+    /** @var IUserSession|MockObject */
115
+    protected $userSession;
116
+    /** @var KnownUserService|MockObject */
117
+    protected $knownUserService;
118
+    /** @var ShareDisableChecker|MockObject */
119
+    protected $shareDisabledChecker;
120
+    private DateTimeZone $timezone;
121
+    /** @var IDateTimeZone|MockObject */
122
+    protected $dateTimeZone;
123
+    /** @var IAppConfig|MockObject */
124
+    protected $appConfig;
125
+
126
+    protected function setUp(): void {
127
+        $this->logger = $this->createMock(LoggerInterface::class);
128
+        $this->config = $this->createMock(IConfig::class);
129
+        $this->secureRandom = $this->createMock(ISecureRandom::class);
130
+        $this->hasher = $this->createMock(IHasher::class);
131
+        $this->mountManager = $this->createMock(IMountManager::class);
132
+        $this->groupManager = $this->createMock(IGroupManager::class);
133
+        $this->userManager = $this->createMock(IUserManager::class);
134
+        $this->rootFolder = $this->createMock(IRootFolder::class);
135
+        $this->mailer = $this->createMock(IMailer::class);
136
+        $this->urlGenerator = $this->createMock(IURLGenerator::class);
137
+        $this->defaults = $this->createMock(\OC_Defaults::class);
138
+        $this->dispatcher = $this->createMock(IEventDispatcher::class);
139
+        $this->userSession = $this->createMock(IUserSession::class);
140
+        $this->knownUserService = $this->createMock(KnownUserService::class);
141
+
142
+        $this->shareDisabledChecker = new ShareDisableChecker($this->config, $this->userManager, $this->groupManager);
143
+        $this->dateTimeZone = $this->createMock(IDateTimeZone::class);
144
+        $this->timezone = new \DateTimeZone('Pacific/Auckland');
145
+        $this->dateTimeZone->method('getTimeZone')->willReturnCallback(fn () => $this->timezone);
146
+
147
+        $this->appConfig = $this->createMock(IAppConfig::class);
148
+
149
+        $this->l10nFactory = $this->createMock(IFactory::class);
150
+        $this->l = $this->createMock(IL10N::class);
151
+        $this->l->method('t')
152
+            ->willReturnCallback(function ($text, $parameters = []) {
153
+                return vsprintf($text, $parameters);
154
+            });
155
+        $this->l->method('n')
156
+            ->willReturnCallback(function ($singular, $plural, $count, $parameters = []) {
157
+                return vsprintf(str_replace('%n', $count, ($count === 1) ? $singular : $plural), $parameters);
158
+            });
159
+        $this->l10nFactory->method('get')->willReturn($this->l);
160
+
161
+        $this->factory = new DummyFactory(\OC::$server);
162
+
163
+        $this->manager = $this->createManager($this->factory);
164
+
165
+        $this->defaultProvider = $this->createMock(DefaultShareProvider::class);
166
+        $this->defaultProvider->method('identifier')->willReturn('default');
167
+        $this->factory->setProvider($this->defaultProvider);
168
+    }
169
+
170
+    private function createManager(IProviderFactory $factory): Manager {
171
+        return new Manager(
172
+            $this->logger,
173
+            $this->config,
174
+            $this->secureRandom,
175
+            $this->hasher,
176
+            $this->mountManager,
177
+            $this->groupManager,
178
+            $this->l10nFactory,
179
+            $factory,
180
+            $this->userManager,
181
+            $this->rootFolder,
182
+            $this->mailer,
183
+            $this->urlGenerator,
184
+            $this->defaults,
185
+            $this->dispatcher,
186
+            $this->userSession,
187
+            $this->knownUserService,
188
+            $this->shareDisabledChecker,
189
+            $this->dateTimeZone,
190
+            $this->appConfig,
191
+        );
192
+    }
193
+
194
+    /**
195
+     * @return MockBuilder
196
+     */
197
+    private function createManagerMock() {
198
+        return $this->getMockBuilder(Manager::class)
199
+            ->setConstructorArgs([
200
+                $this->logger,
201
+                $this->config,
202
+                $this->secureRandom,
203
+                $this->hasher,
204
+                $this->mountManager,
205
+                $this->groupManager,
206
+                $this->l10nFactory,
207
+                $this->factory,
208
+                $this->userManager,
209
+                $this->rootFolder,
210
+                $this->mailer,
211
+                $this->urlGenerator,
212
+                $this->defaults,
213
+                $this->dispatcher,
214
+                $this->userSession,
215
+                $this->knownUserService,
216
+                $this->shareDisabledChecker,
217
+                $this->dateTimeZone,
218
+                $this->appConfig,
219
+            ]);
220
+    }
221
+
222
+    private function createFolderMock(string $folderPath): MockObject&Folder {
223
+        $folder = $this->createMock(Folder::class);
224
+        $folder->method('getPath')->willReturn($folderPath);
225
+        $folder->method('getRelativePath')->willReturnCallback(
226
+            fn (string $path): ?string => PathHelper::getRelativePath($folderPath, $path)
227
+        );
228
+        return $folder;
229
+    }
230
+
231
+    public function testDeleteNoShareId(): void {
232
+        $this->expectException(\InvalidArgumentException::class);
233
+
234
+        $share = $this->manager->newShare();
235
+
236
+        $this->manager->deleteShare($share);
237
+    }
238
+
239
+    public static function dataTestDelete(): array {
240
+        return [
241
+            [IShare::TYPE_USER, 'sharedWithUser'],
242
+            [IShare::TYPE_GROUP, 'sharedWithGroup'],
243
+            [IShare::TYPE_LINK, ''],
244
+            [IShare::TYPE_REMOTE, '[email protected]'],
245
+        ];
246
+    }
247
+
248
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataTestDelete')]
249
+    public function testDelete($shareType, $sharedWith): void {
250
+        $manager = $this->createManagerMock()
251
+            ->onlyMethods(['getShareById', 'deleteChildren', 'promoteReshares'])
252
+            ->getMock();
253
+
254
+        $manager->method('deleteChildren')->willReturn([]);
255
+
256
+        $path = $this->createMock(File::class);
257
+        $path->method('getId')->willReturn(1);
258
+
259
+        $share = $this->manager->newShare();
260
+        $share->setId(42)
261
+            ->setProviderId('prov')
262
+            ->setShareType($shareType)
263
+            ->setSharedWith($sharedWith)
264
+            ->setSharedBy('sharedBy')
265
+            ->setNode($path)
266
+            ->setTarget('myTarget');
267
+
268
+        $manager->expects($this->once())->method('deleteChildren')->with($share);
269
+        $manager->expects($this->once())->method('promoteReshares')->with($share);
270
+
271
+        $this->defaultProvider
272
+            ->expects($this->once())
273
+            ->method('delete')
274
+            ->with($share);
275
+
276
+        $calls = [
277
+            BeforeShareDeletedEvent::class,
278
+            ShareDeletedEvent::class,
279
+        ];
280
+        $this->dispatcher->expects($this->exactly(2))
281
+            ->method('dispatchTyped')
282
+            ->willReturnCallback(function ($event) use (&$calls, $share): void {
283
+                $expected = array_shift($calls);
284
+                $this->assertInstanceOf($expected, $event);
285
+                $this->assertEquals($share, $event->getShare());
286
+            });
287
+
288
+        $manager->deleteShare($share);
289
+    }
290
+
291
+    public function testDeleteLazyShare(): void {
292
+        $manager = $this->createManagerMock()
293
+            ->onlyMethods(['getShareById', 'deleteChildren', 'promoteReshares'])
294
+            ->getMock();
295
+
296
+        $manager->method('deleteChildren')->willReturn([]);
297
+
298
+        $share = $this->manager->newShare();
299
+        $share->setId(42)
300
+            ->setProviderId('prov')
301
+            ->setShareType(IShare::TYPE_USER)
302
+            ->setSharedWith('sharedWith')
303
+            ->setSharedBy('sharedBy')
304
+            ->setShareOwner('shareOwner')
305
+            ->setTarget('myTarget')
306
+            ->setNodeId(1)
307
+            ->setNodeType('file');
308
+
309
+        $this->rootFolder->expects($this->never())->method($this->anything());
310
+
311
+        $manager->expects($this->once())->method('deleteChildren')->with($share);
312
+        $manager->expects($this->once())->method('promoteReshares')->with($share);
313
+
314
+        $this->defaultProvider
315
+            ->expects($this->once())
316
+            ->method('delete')
317
+            ->with($share);
318
+
319
+        $calls = [
320
+            BeforeShareDeletedEvent::class,
321
+            ShareDeletedEvent::class,
322
+        ];
323
+        $this->dispatcher->expects($this->exactly(2))
324
+            ->method('dispatchTyped')
325
+            ->willReturnCallback(function ($event) use (&$calls, $share): void {
326
+                $expected = array_shift($calls);
327
+                $this->assertInstanceOf($expected, $event);
328
+                $this->assertEquals($share, $event->getShare());
329
+            });
330
+
331
+        $manager->deleteShare($share);
332
+    }
333
+
334
+    public function testDeleteNested(): void {
335
+        $manager = $this->createManagerMock()
336
+            ->onlyMethods(['getShareById', 'promoteReshares'])
337
+            ->getMock();
338
+
339
+        $path = $this->createMock(File::class);
340
+        $path->method('getId')->willReturn(1);
341
+
342
+        $share1 = $this->manager->newShare();
343
+        $share1->setId(42)
344
+            ->setProviderId('prov')
345
+            ->setShareType(IShare::TYPE_USER)
346
+            ->setSharedWith('sharedWith1')
347
+            ->setSharedBy('sharedBy1')
348
+            ->setNode($path)
349
+            ->setTarget('myTarget1');
350
+
351
+        $share2 = $this->manager->newShare();
352
+        $share2->setId(43)
353
+            ->setProviderId('prov')
354
+            ->setShareType(IShare::TYPE_GROUP)
355
+            ->setSharedWith('sharedWith2')
356
+            ->setSharedBy('sharedBy2')
357
+            ->setNode($path)
358
+            ->setTarget('myTarget2')
359
+            ->setParent(42);
360
+
361
+        $share3 = $this->manager->newShare();
362
+        $share3->setId(44)
363
+            ->setProviderId('prov')
364
+            ->setShareType(IShare::TYPE_LINK)
365
+            ->setSharedBy('sharedBy3')
366
+            ->setNode($path)
367
+            ->setTarget('myTarget3')
368
+            ->setParent(43);
369
+
370
+        $this->defaultProvider
371
+            ->method('getChildren')
372
+            ->willReturnMap([
373
+                [$share1, [$share2]],
374
+                [$share2, [$share3]],
375
+                [$share3, []],
376
+            ]);
377
+
378
+        $deleteCalls = [
379
+            $share3,
380
+            $share2,
381
+            $share1,
382
+        ];
383
+        $this->defaultProvider->expects($this->exactly(3))
384
+            ->method('delete')
385
+            ->willReturnCallback(function ($share) use (&$deleteCalls): void {
386
+                $expected = array_shift($deleteCalls);
387
+                $this->assertEquals($expected, $share);
388
+            });
389
+
390
+        $dispatchCalls = [
391
+            [BeforeShareDeletedEvent::class, $share1],
392
+            [BeforeShareDeletedEvent::class, $share2],
393
+            [BeforeShareDeletedEvent::class, $share3],
394
+            [ShareDeletedEvent::class, $share3],
395
+            [ShareDeletedEvent::class, $share2],
396
+            [ShareDeletedEvent::class, $share1],
397
+        ];
398
+        $this->dispatcher->expects($this->exactly(6))
399
+            ->method('dispatchTyped')
400
+            ->willReturnCallback(function ($event) use (&$dispatchCalls): void {
401
+                $expected = array_shift($dispatchCalls);
402
+                $this->assertInstanceOf($expected[0], $event);
403
+                $this->assertEquals($expected[1]->getId(), $event->getShare()->getId());
404
+            });
405
+
406
+        $manager->deleteShare($share1);
407
+    }
408
+
409
+    public function testDeleteFromSelf(): void {
410
+        $manager = $this->createManagerMock()
411
+            ->onlyMethods(['getShareById'])
412
+            ->getMock();
413
+
414
+        $recipientId = 'unshareFrom';
415
+        $share = $this->manager->newShare();
416
+        $share->setId(42)
417
+            ->setProviderId('prov')
418
+            ->setShareType(IShare::TYPE_USER)
419
+            ->setSharedWith('sharedWith')
420
+            ->setSharedBy('sharedBy')
421
+            ->setShareOwner('shareOwner')
422
+            ->setTarget('myTarget')
423
+            ->setNodeId(1)
424
+            ->setNodeType('file');
425
+
426
+        $this->defaultProvider
427
+            ->expects($this->once())
428
+            ->method('deleteFromSelf')
429
+            ->with($share, $recipientId);
430
+
431
+        $this->dispatcher->expects($this->once())
432
+            ->method('dispatchTyped')
433
+            ->with(
434
+                $this->callBack(function (ShareDeletedFromSelfEvent $e) use ($share) {
435
+                    return $e->getShare() === $share;
436
+                })
437
+            );
438
+
439
+        $manager->deleteFromSelf($share, $recipientId);
440
+    }
441
+
442
+    public function testDeleteChildren(): void {
443
+        $manager = $this->createManagerMock()
444
+            ->onlyMethods(['deleteShare'])
445
+            ->getMock();
446
+
447
+        $share = $this->createMock(IShare::class);
448
+        $share->method('getShareType')->willReturn(IShare::TYPE_USER);
449
+
450
+        $child1 = $this->createMock(IShare::class);
451
+        $child1->method('getShareType')->willReturn(IShare::TYPE_USER);
452
+        $child2 = $this->createMock(IShare::class);
453
+        $child2->method('getShareType')->willReturn(IShare::TYPE_USER);
454
+        $child3 = $this->createMock(IShare::class);
455
+        $child3->method('getShareType')->willReturn(IShare::TYPE_USER);
456
+
457
+        $shares = [
458
+            $child1,
459
+            $child2,
460
+            $child3,
461
+        ];
462
+
463
+        $this->defaultProvider
464
+            ->expects($this->exactly(4))
465
+            ->method('getChildren')
466
+            ->willReturnCallback(function ($_share) use ($share, $shares) {
467
+                if ($_share === $share) {
468
+                    return $shares;
469
+                }
470
+                return [];
471
+            });
472
+
473
+        $calls = [
474
+            $child1,
475
+            $child2,
476
+            $child3,
477
+        ];
478
+        $this->defaultProvider->expects($this->exactly(3))
479
+            ->method('delete')
480
+            ->willReturnCallback(function ($share) use (&$calls): void {
481
+                $expected = array_shift($calls);
482
+                $this->assertEquals($expected, $share);
483
+            });
484
+
485
+        $result = self::invokePrivate($manager, 'deleteChildren', [$share]);
486
+        $this->assertSame($shares, $result);
487
+    }
488
+
489
+    public function testPromoteReshareFile(): void {
490
+        $manager = $this->createManagerMock()
491
+            ->onlyMethods(['updateShare', 'getSharesInFolder', 'generalCreateChecks'])
492
+            ->getMock();
493
+
494
+        $file = $this->createMock(File::class);
495
+
496
+        $share = $this->createMock(IShare::class);
497
+        $share->method('getShareType')->willReturn(IShare::TYPE_USER);
498
+        $share->method('getNodeType')->willReturn('folder');
499
+        $share->method('getSharedWith')->willReturn('userB');
500
+        $share->method('getNode')->willReturn($file);
501
+
502
+        $reShare = $this->createMock(IShare::class);
503
+        $reShare->method('getShareType')->willReturn(IShare::TYPE_USER);
504
+        $reShare->method('getSharedBy')->willReturn('userB');
505
+        $reShare->method('getSharedWith')->willReturn('userC');
506
+        $reShare->method('getNode')->willReturn($file);
507
+
508
+        $this->defaultProvider->method('getSharesBy')
509
+            ->willReturnCallback(function ($userId, $shareType, $node, $reshares, $limit, $offset) use ($reShare, $file) {
510
+                $this->assertEquals($file, $node);
511
+                if ($shareType === IShare::TYPE_USER) {
512
+                    return match($userId) {
513
+                        'userB' => [$reShare],
514
+                    };
515
+                } else {
516
+                    return [];
517
+                }
518
+            });
519
+        $manager->method('generalCreateChecks')->willThrowException(new GenericShareException());
520
+
521
+        $manager->expects($this->exactly(1))->method('updateShare')->with($reShare);
522
+
523
+        self::invokePrivate($manager, 'promoteReshares', [$share]);
524
+    }
525
+
526
+    public function testPromoteReshare(): void {
527
+        $manager = $this->createManagerMock()
528
+            ->onlyMethods(['updateShare', 'getSharesInFolder', 'generalCreateChecks'])
529
+            ->getMock();
530
+
531
+        $folder = $this->createFolderMock('/path/to/folder');
532
+
533
+        $subFolder = $this->createFolderMock('/path/to/folder/sub');
534
+
535
+        $otherFolder = $this->createFolderMock('/path/to/otherfolder/');
536
+
537
+        $share = $this->createMock(IShare::class);
538
+        $share->method('getShareType')->willReturn(IShare::TYPE_USER);
539
+        $share->method('getNodeType')->willReturn('folder');
540
+        $share->method('getSharedWith')->willReturn('userB');
541
+        $share->method('getNode')->willReturn($folder);
542
+
543
+        $reShare = $this->createMock(IShare::class);
544
+        $reShare->method('getShareType')->willReturn(IShare::TYPE_USER);
545
+        $reShare->method('getSharedBy')->willReturn('userB');
546
+        $reShare->method('getSharedWith')->willReturn('userC');
547
+        $reShare->method('getNode')->willReturn($folder);
548
+
549
+        $reShareInSubFolder = $this->createMock(IShare::class);
550
+        $reShareInSubFolder->method('getShareType')->willReturn(IShare::TYPE_USER);
551
+        $reShareInSubFolder->method('getSharedBy')->willReturn('userB');
552
+        $reShareInSubFolder->method('getNode')->willReturn($subFolder);
553
+
554
+        $reShareInOtherFolder = $this->createMock(IShare::class);
555
+        $reShareInOtherFolder->method('getShareType')->willReturn(IShare::TYPE_USER);
556
+        $reShareInOtherFolder->method('getSharedBy')->willReturn('userB');
557
+        $reShareInOtherFolder->method('getNode')->willReturn($otherFolder);
558
+
559
+        $this->defaultProvider->method('getSharesBy')
560
+            ->willReturnCallback(function ($userId, $shareType, $node, $reshares, $limit, $offset) use ($reShare, $reShareInSubFolder, $reShareInOtherFolder) {
561
+                if ($shareType === IShare::TYPE_USER) {
562
+                    return match($userId) {
563
+                        'userB' => [$reShare,$reShareInSubFolder,$reShareInOtherFolder],
564
+                    };
565
+                } else {
566
+                    return [];
567
+                }
568
+            });
569
+        $manager->method('generalCreateChecks')->willThrowException(new GenericShareException());
570
+
571
+        $calls = [
572
+            $reShare,
573
+            $reShareInSubFolder,
574
+        ];
575
+        $manager->expects($this->exactly(2))
576
+            ->method('updateShare')
577
+            ->willReturnCallback(function ($share) use (&$calls): void {
578
+                $expected = array_shift($calls);
579
+                $this->assertEquals($expected, $share);
580
+            });
581
+
582
+        self::invokePrivate($manager, 'promoteReshares', [$share]);
583
+    }
584
+
585
+    public function testPromoteReshareWhenUserHasAnotherShare(): void {
586
+        $manager = $this->createManagerMock()
587
+            ->onlyMethods(['updateShare', 'getSharesInFolder', 'getSharedWith', 'generalCreateChecks'])
588
+            ->getMock();
589
+
590
+        $folder = $this->createFolderMock('/path/to/folder');
591
+
592
+        $share = $this->createMock(IShare::class);
593
+        $share->method('getShareType')->willReturn(IShare::TYPE_USER);
594
+        $share->method('getNodeType')->willReturn('folder');
595
+        $share->method('getSharedWith')->willReturn('userB');
596
+        $share->method('getNode')->willReturn($folder);
597
+
598
+        $reShare = $this->createMock(IShare::class);
599
+        $reShare->method('getShareType')->willReturn(IShare::TYPE_USER);
600
+        $reShare->method('getNodeType')->willReturn('folder');
601
+        $reShare->method('getSharedBy')->willReturn('userB');
602
+        $reShare->method('getNode')->willReturn($folder);
603
+
604
+        $this->defaultProvider->method('getSharesBy')->willReturn([$reShare]);
605
+        $manager->method('generalCreateChecks')->willReturn(true);
606
+
607
+        /* No share is promoted because generalCreateChecks does not throw */
608
+        $manager->expects($this->never())->method('updateShare');
609
+
610
+        self::invokePrivate($manager, 'promoteReshares', [$share]);
611
+    }
612
+
613
+    public function testPromoteReshareOfUsersInGroupShare(): void {
614
+        $manager = $this->createManagerMock()
615
+            ->onlyMethods(['updateShare', 'getSharesInFolder', 'getSharedWith', 'generalCreateChecks'])
616
+            ->getMock();
617
+
618
+        $folder = $this->createFolderMock('/path/to/folder');
619
+
620
+        $userA = $this->createMock(IUser::class);
621
+        $userA->method('getUID')->willReturn('userA');
622
+
623
+        $share = $this->createMock(IShare::class);
624
+        $share->method('getShareType')->willReturn(IShare::TYPE_GROUP);
625
+        $share->method('getNodeType')->willReturn('folder');
626
+        $share->method('getSharedWith')->willReturn('Group');
627
+        $share->method('getNode')->willReturn($folder);
628
+        $share->method('getShareOwner')->willReturn($userA);
629
+
630
+        $reShare1 = $this->createMock(IShare::class);
631
+        $reShare1->method('getShareType')->willReturn(IShare::TYPE_USER);
632
+        $reShare1->method('getNodeType')->willReturn('folder');
633
+        $reShare1->method('getSharedBy')->willReturn('userB');
634
+        $reShare1->method('getNode')->willReturn($folder);
635
+
636
+        $reShare2 = $this->createMock(IShare::class);
637
+        $reShare2->method('getShareType')->willReturn(IShare::TYPE_USER);
638
+        $reShare2->method('getNodeType')->willReturn('folder');
639
+        $reShare2->method('getSharedBy')->willReturn('userC');
640
+        $reShare2->method('getNode')->willReturn($folder);
641
+
642
+        $userB = $this->createMock(IUser::class);
643
+        $userB->method('getUID')->willReturn('userB');
644
+        $userC = $this->createMock(IUser::class);
645
+        $userC->method('getUID')->willReturn('userC');
646
+        $group = $this->createMock(IGroup::class);
647
+        $group->method('getUsers')->willReturn([$userB, $userC]);
648
+        $this->groupManager->method('get')->with('Group')->willReturn($group);
649
+
650
+        $this->defaultProvider->method('getSharesBy')
651
+            ->willReturnCallback(function ($userId, $shareType, $node, $reshares, $limit, $offset) use ($reShare1, $reShare2) {
652
+                if ($shareType === IShare::TYPE_USER) {
653
+                    return match($userId) {
654
+                        'userB' => [$reShare1],
655
+                        'userC' => [$reShare2],
656
+                    };
657
+                } else {
658
+                    return [];
659
+                }
660
+            });
661
+        $manager->method('generalCreateChecks')->willThrowException(new GenericShareException());
662
+
663
+        $manager->method('getSharedWith')->willReturn([]);
664
+
665
+        $calls = [
666
+            $reShare1,
667
+            $reShare2,
668
+        ];
669
+        $manager->expects($this->exactly(2))
670
+            ->method('updateShare')
671
+            ->willReturnCallback(function ($share) use (&$calls): void {
672
+                $expected = array_shift($calls);
673
+                $this->assertEquals($expected, $share);
674
+            });
675
+
676
+        self::invokePrivate($manager, 'promoteReshares', [$share]);
677
+    }
678
+
679
+    public function testGetShareById(): void {
680
+        $share = $this->createMock(IShare::class);
681
+
682
+        $this->defaultProvider
683
+            ->expects($this->once())
684
+            ->method('getShareById')
685
+            ->with(42)
686
+            ->willReturn($share);
687
+
688
+        $this->assertEquals($share, $this->manager->getShareById('default:42'));
689
+    }
690
+
691
+
692
+    public function testGetExpiredShareById(): void {
693
+        $this->expectException(ShareNotFound::class);
694
+
695
+        $manager = $this->createManagerMock()
696
+            ->onlyMethods(['deleteShare'])
697
+            ->getMock();
698
+
699
+        $date = new \DateTime();
700
+        $date->setTime(0, 0, 0);
701
+
702
+        $share = $this->manager->newShare();
703
+        $share->setExpirationDate($date)
704
+            ->setShareType(IShare::TYPE_LINK);
705
+
706
+        $this->defaultProvider->expects($this->once())
707
+            ->method('getShareById')
708
+            ->with('42')
709
+            ->willReturn($share);
710
+
711
+        $manager->expects($this->once())
712
+            ->method('deleteShare')
713
+            ->with($share);
714
+
715
+        $manager->getShareById('default:42');
716
+    }
717
+
718
+
719
+    public function testVerifyPasswordNullButEnforced(): void {
720
+        $this->expectException(\InvalidArgumentException::class);
721
+        $this->expectExceptionMessage('Passwords are enforced for link and mail shares');
722
+
723
+        $this->config->method('getAppValue')->willReturnMap([
724
+            ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
725
+        ]);
726
+
727
+        $this->appConfig->method('getValueBool')->willReturnMap([
728
+            ['core', 'shareapi_enforce_links_password', true],
729
+        ]);
730
+
731
+        self::invokePrivate($this->manager, 'verifyPassword', [null]);
732
+    }
733
+
734
+    public function testVerifyPasswordNotEnforcedGroup(): void {
735
+        $this->config->method('getAppValue')->willReturnMap([
736
+            ['core', 'shareapi_enforce_links_password_excluded_groups', '', '["admin"]'],
737
+            ['core', 'shareapi_enforce_links_password', 'no', 'yes'],
738
+        ]);
739
+
740
+        // Create admin user
741
+        $user = $this->createMock(IUser::class);
742
+        $this->userSession->method('getUser')->willReturn($user);
743
+        $this->groupManager->method('getUserGroupIds')->with($user)->willReturn(['admin']);
744
+
745
+        $result = self::invokePrivate($this->manager, 'verifyPassword', [null]);
746
+        $this->assertNull($result);
747
+    }
748
+
749
+    public function testVerifyPasswordNotEnforcedMultipleGroups(): void {
750
+        $this->config->method('getAppValue')->willReturnMap([
751
+            ['core', 'shareapi_enforce_links_password_excluded_groups', '', '["admin", "special"]'],
752
+            ['core', 'shareapi_enforce_links_password', 'no', 'yes'],
753
+        ]);
754
+
755
+        // Create admin user
756
+        $user = $this->createMock(IUser::class);
757
+        $this->userSession->method('getUser')->willReturn($user);
758
+        $this->groupManager->method('getUserGroupIds')->with($user)->willReturn(['special']);
759
+
760
+        $result = self::invokePrivate($this->manager, 'verifyPassword', [null]);
761
+        $this->assertNull($result);
762
+    }
763
+
764
+    public function testVerifyPasswordNull(): void {
765
+        $this->config->method('getAppValue')->willReturnMap([
766
+            ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
767
+            ['core', 'shareapi_enforce_links_password', 'no', 'no'],
768
+        ]);
769
+
770
+        $result = self::invokePrivate($this->manager, 'verifyPassword', [null]);
771
+        $this->assertNull($result);
772
+    }
773
+
774
+    public function testVerifyPasswordHook(): void {
775
+        $this->config->method('getAppValue')->willReturnMap([
776
+            ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
777
+            ['core', 'shareapi_enforce_links_password', 'no', 'no'],
778
+        ]);
779
+
780
+        $this->dispatcher->expects($this->once())->method('dispatchTyped')
781
+            ->willReturnCallback(function (Event $event): void {
782
+                $this->assertInstanceOf(ValidatePasswordPolicyEvent::class, $event);
783
+                /** @var ValidatePasswordPolicyEvent $event */
784
+                $this->assertSame('password', $event->getPassword());
785
+            }
786
+            );
787
+
788
+        $result = self::invokePrivate($this->manager, 'verifyPassword', ['password']);
789
+        $this->assertNull($result);
790
+    }
791
+
792
+
793
+    public function testVerifyPasswordHookFails(): void {
794
+        $this->expectException(\Exception::class);
795
+        $this->expectExceptionMessage('password not accepted');
796
+
797
+        $this->config->method('getAppValue')->willReturnMap([
798
+            ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
799
+            ['core', 'shareapi_enforce_links_password', 'no', 'no'],
800
+        ]);
801
+
802
+        $this->dispatcher->expects($this->once())->method('dispatchTyped')
803
+            ->willReturnCallback(function (Event $event): void {
804
+                $this->assertInstanceOf(ValidatePasswordPolicyEvent::class, $event);
805
+                /** @var ValidatePasswordPolicyEvent $event */
806
+                $this->assertSame('password', $event->getPassword());
807
+                throw new HintException('password not accepted');
808
+            }
809
+            );
810
+
811
+        self::invokePrivate($this->manager, 'verifyPassword', ['password']);
812
+    }
813
+
814
+    public function createShare($id, $type, $node, $sharedWith, $sharedBy, $shareOwner,
815
+        $permissions, $expireDate = null, $password = null, $attributes = null) {
816
+        $share = $this->createMock(IShare::class);
817
+
818
+        $share->method('getShareType')->willReturn($type);
819
+        $share->method('getSharedWith')->willReturn($sharedWith);
820
+        $share->method('getSharedBy')->willReturn($sharedBy);
821
+        $share->method('getShareOwner')->willReturn($shareOwner);
822
+        $share->method('getNode')->willReturn($node);
823
+        if ($node && $node->getId()) {
824
+            $share->method('getNodeId')->willReturn($node->getId());
825
+        }
826
+        $share->method('getPermissions')->willReturn($permissions);
827
+        $share->method('getAttributes')->willReturn($attributes);
828
+        $share->method('getExpirationDate')->willReturn($expireDate);
829
+        $share->method('getPassword')->willReturn($password);
830
+
831
+        return $share;
832
+    }
833
+
834
+    public static function dataGeneralChecks(): array {
835
+        $user0 = 'user0';
836
+        $user2 = 'user1';
837
+        $group0 = 'group0';
838
+
839
+        $file = [
840
+            File::class,
841
+            [
842
+                'getId' => 108,
843
+            ],
844
+            'default',
845
+        ];
846
+
847
+        $node = [
848
+            Node::class,
849
+            [
850
+                'getId' => 108,
851
+            ],
852
+            'default',
853
+        ];
854
+
855
+        $data = [
856
+            [[null, IShare::TYPE_USER, $file, null, $user0, $user0, 31, null, null], 'Share recipient is not a valid user', true],
857
+            [[null, IShare::TYPE_USER, $file, $group0, $user0, $user0, 31, null, null], 'Share recipient is not a valid user', true],
858
+            [[null, IShare::TYPE_USER, $file, '[email protected]', $user0, $user0, 31, null, null], 'Share recipient is not a valid user', true],
859
+            [[null, IShare::TYPE_GROUP, $file, null, $user0, $user0, 31, null, null], 'Share recipient is not a valid group', true],
860
+            [[null, IShare::TYPE_GROUP, $file, $user2, $user0, $user0, 31, null, null], 'Share recipient is not a valid group', true],
861
+            [[null, IShare::TYPE_GROUP, $file, '[email protected]', $user0, $user0, 31, null, null], 'Share recipient is not a valid group', true],
862
+            [[null, IShare::TYPE_LINK, $file, $user2, $user0, $user0, 31, null, null], 'Share recipient should be empty', true],
863
+            [[null, IShare::TYPE_LINK, $file, $group0, $user0, $user0, 31, null, null], 'Share recipient should be empty', true],
864
+            [[null, IShare::TYPE_LINK, $file, '[email protected]', $user0, $user0, 31, null, null], 'Share recipient should be empty', true],
865
+            [[null, -1, $file, null, $user0, $user0, 31, null, null], 'Unknown share type', true],
866
+
867
+            [[null, IShare::TYPE_USER, $file, $user2, null, $user0, 31, null, null], 'Share initiator must be set', true],
868
+            [[null, IShare::TYPE_GROUP, $file, $group0, null, $user0, 31, null, null], 'Share initiator must be set', true],
869
+            [[null, IShare::TYPE_LINK, $file, null, null, $user0, 31, null, null], 'Share initiator must be set', true],
870
+
871
+            [[null, IShare::TYPE_USER, $file, $user0, $user0, $user0, 31, null, null], 'Cannot share with yourself', true],
872
+
873
+            [[null, IShare::TYPE_USER, null, $user2, $user0, $user0, 31, null, null], 'Shared path must be set', true],
874
+            [[null, IShare::TYPE_GROUP, null, $group0, $user0, $user0, 31, null, null], 'Shared path must be set', true],
875
+            [[null, IShare::TYPE_LINK, null, null, $user0, $user0, 31, null, null], 'Shared path must be set', true],
876
+
877
+            [[null, IShare::TYPE_USER, $node, $user2, $user0, $user0, 31, null, null], 'Shared path must be either a file or a folder', true],
878
+            [[null, IShare::TYPE_GROUP, $node, $group0, $user0, $user0, 31, null, null], 'Shared path must be either a file or a folder', true],
879
+            [[null, IShare::TYPE_LINK, $node, null, $user0, $user0, 31, null, null], 'Shared path must be either a file or a folder', true],
880
+        ];
881
+
882
+        $nonShareAble = [
883
+            Folder::class,
884
+            [
885
+                'getId' => 108,
886
+                'isShareable' => false,
887
+                'getPath' => 'path',
888
+                'getName' => 'name',
889
+                'getOwner' => $user0,
890
+            ],
891
+            'default',
892
+        ];
893
+
894
+        $data[] = [[null, IShare::TYPE_USER, $nonShareAble, $user2, $user0, $user0, 31, null, null], 'You are not allowed to share name', true];
895
+        $data[] = [[null, IShare::TYPE_GROUP, $nonShareAble, $group0, $user0, $user0, 31, null, null], 'You are not allowed to share name', true];
896
+        $data[] = [[null, IShare::TYPE_LINK, $nonShareAble, null, $user0, $user0, 31, null, null], 'You are not allowed to share name', true];
897
+
898
+        $limitedPermssions = [
899
+            File::class,
900
+            [
901
+                'isShareable' => true,
902
+                'getPermissions' => Constants::PERMISSION_READ,
903
+                'getId' => 108,
904
+                'getPath' => 'path',
905
+                'getName' => 'name',
906
+                'getOwner' => $user0,
907
+            ],
908
+            'default',
909
+        ];
910
+
911
+        $data[] = [[null, IShare::TYPE_USER, $limitedPermssions, $user2, $user0, $user0, null, null, null], 'Valid permissions are required for sharing', true];
912
+        $data[] = [[null, IShare::TYPE_GROUP, $limitedPermssions, $group0, $user0, $user0, null, null, null], 'Valid permissions are required for sharing', true];
913
+        $data[] = [[null, IShare::TYPE_LINK, $limitedPermssions, null, $user0, $user0, null, null, null], 'Valid permissions are required for sharing', true];
914
+
915
+        $limitedPermssions[1]['getMountPoint'] = MoveableMount::class;
916
+
917
+        // increase permissions of a re-share
918
+        $data[] = [[null, IShare::TYPE_GROUP, $limitedPermssions, $group0, $user0, $user0, 17, null, null], 'Cannot increase permissions of path', true];
919
+        $data[] = [[null, IShare::TYPE_USER, $limitedPermssions, $user2, $user0, $user0, 3, null, null], 'Cannot increase permissions of path', true];
920
+
921
+        $nonMoveableMountPermssions = [
922
+            Folder::class,
923
+            [
924
+                'isShareable' => true,
925
+                'getPermissions' => Constants::PERMISSION_READ,
926
+                'getId' => 108,
927
+                'getPath' => 'path',
928
+                'getName' => 'name',
929
+                'getInternalPath' => '',
930
+                'getOwner' => $user0,
931
+            ],
932
+            'allPermssions',
933
+        ];
934
+
935
+        $data[] = [[null, IShare::TYPE_USER, $nonMoveableMountPermssions, $user2, $user0, $user0, 11, null, null], 'Cannot increase permissions of path', false];
936
+        $data[] = [[null, IShare::TYPE_GROUP, $nonMoveableMountPermssions, $group0, $user0, $user0, 11, null, null], 'Cannot increase permissions of path', false];
937
+
938
+        $rootFolder = [
939
+            Folder::class,
940
+            [
941
+                'isShareable' => true,
942
+                'getPermissions' => Constants::PERMISSION_ALL,
943
+                'getId' => 42,
944
+            ],
945
+            'none',
946
+        ];
947
+
948
+        $data[] = [[null, IShare::TYPE_USER, $rootFolder, $user2, $user0, $user0, 30, null, null], 'You cannot share your root folder', true];
949
+        $data[] = [[null, IShare::TYPE_GROUP, $rootFolder, $group0, $user0, $user0, 2, null, null], 'You cannot share your root folder', true];
950
+        $data[] = [[null, IShare::TYPE_LINK, $rootFolder, null, $user0, $user0, 16, null, null], 'You cannot share your root folder', true];
951
+
952
+        $allPermssionsFiles = [
953
+            File::class,
954
+            [
955
+                'isShareable' => true,
956
+                'getPermissions' => Constants::PERMISSION_ALL,
957
+                'getId' => 187,
958
+                'getOwner' => $user0,
959
+            ],
960
+            'default',
961
+        ];
962
+
963
+        // test invalid CREATE or DELETE permissions
964
+        $data[] = [[null, IShare::TYPE_USER, $allPermssionsFiles, $user2, $user0, $user0, Constants::PERMISSION_ALL, null, null], 'File shares cannot have create or delete permissions', true];
965
+        $data[] = [[null, IShare::TYPE_GROUP, $allPermssionsFiles, $group0, $user0, $user0, Constants::PERMISSION_READ | Constants::PERMISSION_CREATE, null, null], 'File shares cannot have create or delete permissions', true];
966
+        $data[] = [[null, IShare::TYPE_LINK, $allPermssionsFiles, null, $user0, $user0, Constants::PERMISSION_READ | Constants::PERMISSION_DELETE, null, null], 'File shares cannot have create or delete permissions', true];
967
+
968
+        $allPermssions = [
969
+            Folder::class,
970
+            [
971
+                'isShareable' => true,
972
+                'getPermissions' => Constants::PERMISSION_ALL,
973
+                'getId' => 108,
974
+                'getOwner' => $user0,
975
+            ],
976
+            'default',
977
+        ];
978
+
979
+        $data[] = [[null, IShare::TYPE_USER, $allPermssions, $user2, $user0, $user0, 30, null, null], 'Shares need at least read permissions', true];
980
+        $data[] = [[null, IShare::TYPE_GROUP, $allPermssions, $group0, $user0, $user0, 2, null, null], 'Shares need at least read permissions', true];
981
+
982
+        // test invalid permissions
983
+        $data[] = [[null, IShare::TYPE_USER, $allPermssions, $user2, $user0, $user0, 32, null, null], 'Valid permissions are required for sharing', true];
984
+        $data[] = [[null, IShare::TYPE_GROUP, $allPermssions, $group0, $user0, $user0, 63, null, null], 'Valid permissions are required for sharing', true];
985
+        $data[] = [[null, IShare::TYPE_LINK, $allPermssions, null, $user0, $user0, -1, null, null], 'Valid permissions are required for sharing', true];
986
+
987
+        // working shares
988
+        $data[] = [[null, IShare::TYPE_USER, $allPermssions, $user2, $user0, $user0, 31, null, null], null, false];
989
+        $data[] = [[null, IShare::TYPE_GROUP, $allPermssions, $group0, $user0, $user0, 3, null, null], null, false];
990
+        $data[] = [[null, IShare::TYPE_LINK, $allPermssions, null, $user0, $user0, 17, null, null], null, false];
991
+
992
+        $remoteFile = [
993
+            Folder::class,
994
+            [
995
+                'isShareable' => true,
996
+                'getPermissions' => Constants::PERMISSION_READ ^ Constants::PERMISSION_UPDATE,
997
+                'getId' => 108,
998
+                'getOwner' => $user0,
999
+            ],
1000
+            'remote',
1001
+        ];
1002
+
1003
+        $data[] = [[null, IShare::TYPE_REMOTE, $remoteFile, $user2, $user0, $user0, 1, null, null], null, false];
1004
+        $data[] = [[null, IShare::TYPE_REMOTE, $remoteFile, $user2, $user0, $user0, 3, null, null], null, false];
1005
+        $data[] = [[null, IShare::TYPE_REMOTE, $remoteFile, $user2, $user0, $user0, 31, null, null], 'Cannot increase permissions of ', true];
1006
+
1007
+        return $data;
1008
+    }
1009
+
1010
+    private function createNodeMock(string $class, array $methods, string $storageType): MockObject {
1011
+        $mock = $this->createMock($class);
1012
+        foreach ($methods as $methodName => $return) {
1013
+            if ($methodName === 'getOwner') {
1014
+                $uid = $return;
1015
+                $return = $this->createMock(IUser::class);
1016
+                $return->method('getUID')
1017
+                    ->willReturn($uid);
1018
+            } elseif ($methodName === 'getMountPoint') {
1019
+                $return = $this->createMock($return);
1020
+            }
1021
+            $mock->method($methodName)->willReturn($return);
1022
+        }
1023
+        switch ($storageType) {
1024
+            case 'default':
1025
+                $storage = $this->createMock(IStorage::class);
1026
+                $storage->method('instanceOfStorage')
1027
+                    ->with('\OCA\Files_Sharing\External\Storage')
1028
+                    ->willReturn(false);
1029
+                break;
1030
+            case 'allPermssions':
1031
+                $storage = $this->createMock(IStorage::class);
1032
+                $storage->method('instanceOfStorage')
1033
+                    ->with('\OCA\Files_Sharing\External\Storage')
1034
+                    ->willReturn(false);
1035
+                $storage->method('getPermissions')->willReturn(Constants::PERMISSION_ALL);
1036
+                break;
1037
+            case 'none':
1038
+                $storage = false;
1039
+                break;
1040
+            case 'remote':
1041
+                $storage = $this->createMock(IStorage::class);
1042
+                $storage->method('instanceOfStorage')
1043
+                    ->with('\OCA\Files_Sharing\External\Storage')
1044
+                    ->willReturn(true);
1045
+                break;
1046
+            default:
1047
+                throw new \Exception('Unknown storage type ' . $storageType);
1048
+        }
1049
+        if ($storage === false) {
1050
+            $mock->expects(self::never())->method('getStorage');
1051
+        } else {
1052
+            $mock->method('getStorage')
1053
+                ->willReturn($storage);
1054
+        }
1055
+
1056
+        return $mock;
1057
+    }
1058
+
1059
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataGeneralChecks')]
1060
+    public function testGeneralChecks(array $shareParams, ?string $exceptionMessage, bool $exception): void {
1061
+        if ($shareParams[2] !== null) {
1062
+            $shareParams[2] = $this->createNodeMock(...$shareParams[2]);
1063
+        }
1064
+        $share = $this->createShare(...$shareParams);
1065
+
1066
+        $thrown = null;
1067
+
1068
+        $this->userManager->method('userExists')->willReturnMap([
1069
+            ['user0', true],
1070
+            ['user1', true],
1071
+        ]);
1072
+
1073
+        $this->groupManager->method('groupExists')->willReturnMap([
1074
+            ['group0', true],
1075
+        ]);
1076
+
1077
+        $userFolder = $this->createMock(Folder::class);
1078
+        $userFolder->expects($this->any())
1079
+            ->method('getId')
1080
+            ->willReturn(42);
1081
+        // Id 108 is used in the data to refer to the node of the share.
1082
+        $userFolder->method('getById')
1083
+            ->with(108)
1084
+            ->willReturn([$share->getNode()]);
1085
+        $userFolder->expects($this->any())
1086
+            ->method('getRelativePath')
1087
+            ->willReturnArgument(0);
1088
+        $this->rootFolder->method('getUserFolder')->willReturn($userFolder);
1089
+
1090
+
1091
+        try {
1092
+            self::invokePrivate($this->manager, 'generalCreateChecks', [$share]);
1093
+            $thrown = false;
1094
+        } catch (GenericShareException $e) {
1095
+            $this->assertEquals($exceptionMessage, $e->getHint());
1096
+            $thrown = true;
1097
+        } catch (\InvalidArgumentException $e) {
1098
+            $this->assertEquals($exceptionMessage, $e->getMessage());
1099
+            $thrown = true;
1100
+        }
1101
+
1102
+        $this->assertSame($exception, $thrown);
1103
+    }
1104
+
1105
+
1106
+    public function testGeneralCheckShareRoot(): void {
1107
+        $this->expectException(\InvalidArgumentException::class);
1108
+        $this->expectExceptionMessage('You cannot share your root folder');
1109
+
1110
+        $thrown = null;
1111
+
1112
+        $this->userManager->method('userExists')->willReturnMap([
1113
+            ['user0', true],
1114
+            ['user1', true],
1115
+        ]);
1116
+
1117
+        $userFolder = $this->createMock(Folder::class);
1118
+        $userFolder->method('isSubNode')->with($userFolder)->willReturn(false);
1119
+        $this->rootFolder->method('getUserFolder')->willReturn($userFolder);
1120
+
1121
+        $share = $this->manager->newShare();
1122
+
1123
+        $share->setShareType(IShare::TYPE_USER)
1124
+            ->setSharedWith('user0')
1125
+            ->setSharedBy('user1')
1126
+            ->setNode($userFolder);
1127
+
1128
+        self::invokePrivate($this->manager, 'generalCreateChecks', [$share]);
1129
+    }
1130
+
1131
+    public static function validateExpirationDateInternalProvider() {
1132
+        return [[IShare::TYPE_USER], [IShare::TYPE_REMOTE], [IShare::TYPE_REMOTE_GROUP]];
1133
+    }
1134
+
1135
+    #[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1136
+    public function testValidateExpirationDateInternalInPast($shareType): void {
1137
+        $this->expectException(GenericShareException::class);
1138
+        $this->expectExceptionMessage('Expiration date is in the past');
1139
+
1140
+        // Expire date in the past
1141
+        $past = new \DateTime();
1142
+        $past->sub(new \DateInterval('P1D'));
1143
+
1144
+        $share = $this->manager->newShare();
1145
+        $share->setShareType($shareType);
1146
+        $share->setExpirationDate($past);
1147
+
1148
+        self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1149
+    }
1150
+
1151
+    #[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1152
+    public function testValidateExpirationDateInternalEnforceButNotSet($shareType): void {
1153
+        $this->expectException(\InvalidArgumentException::class);
1154
+        $this->expectExceptionMessage('Expiration date is enforced');
1155
+
1156
+        $share = $this->manager->newShare();
1157
+        $share->setProviderId('foo')->setId('bar');
1158
+        $share->setShareType($shareType);
1159
+        if ($shareType === IShare::TYPE_USER) {
1160
+            $this->config->method('getAppValue')
1161
+                ->willReturnMap([
1162
+                    ['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
1163
+                    ['core', 'shareapi_enforce_internal_expire_date', 'no', 'yes'],
1164
+                ]);
1165
+        } else {
1166
+            $this->config->method('getAppValue')
1167
+                ->willReturnMap([
1168
+                    ['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
1169
+                    ['core', 'shareapi_enforce_remote_expire_date', 'no', 'yes'],
1170
+                ]);
1171
+        }
1172
+
1173
+        self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1174
+    }
1175
+
1176
+    #[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1177
+    public function testValidateExpirationDateInternalEnforceButNotEnabledAndNotSet($shareType): void {
1178
+        $share = $this->manager->newShare();
1179
+        $share->setProviderId('foo')->setId('bar');
1180
+        $share->setShareType($shareType);
1181
+
1182
+        if ($shareType === IShare::TYPE_USER) {
1183
+            $this->config->method('getAppValue')
1184
+                ->willReturnMap([
1185
+                    ['core', 'shareapi_enforce_internal_expire_date', 'no', 'yes'],
1186
+                ]);
1187
+        } else {
1188
+            $this->config->method('getAppValue')
1189
+                ->willReturnMap([
1190
+                    ['core', 'shareapi_enforce_remote_expire_date', 'no', 'yes'],
1191
+                ]);
1192
+        }
1193
+
1194
+        self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1195
+
1196
+        $this->assertNull($share->getExpirationDate());
1197
+    }
1198
+
1199
+    #[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1200
+    public function testValidateExpirationDateInternalEnforceButNotSetNewShare($shareType): void {
1201
+        $share = $this->manager->newShare();
1202
+        $share->setShareType($shareType);
1203
+
1204
+        if ($shareType === IShare::TYPE_USER) {
1205
+            $this->config->method('getAppValue')
1206
+                ->willReturnMap([
1207
+                    ['core', 'shareapi_enforce_internal_expire_date', 'no', 'yes'],
1208
+                    ['core', 'shareapi_internal_expire_after_n_days', '7', '3'],
1209
+                    ['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
1210
+                    ['core', 'internal_defaultExpDays', '3', '3'],
1211
+                ]);
1212
+        } else {
1213
+            $this->config->method('getAppValue')
1214
+                ->willReturnMap([
1215
+                    ['core', 'shareapi_enforce_remote_expire_date', 'no', 'yes'],
1216
+                    ['core', 'shareapi_remote_expire_after_n_days', '7', '3'],
1217
+                    ['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
1218
+                    ['core', 'remote_defaultExpDays', '3', '3'],
1219
+                ]);
1220
+        }
1221
+
1222
+        $expected = new \DateTime('now', $this->timezone);
1223
+        $expected->setTime(0, 0, 0);
1224
+        $expected->add(new \DateInterval('P3D'));
1225
+
1226
+        self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1227
+
1228
+        $this->assertNotNull($share->getExpirationDate());
1229
+        $this->assertEquals($expected, $share->getExpirationDate());
1230
+    }
1231
+
1232
+    #[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1233
+    public function testValidateExpirationDateInternalEnforceRelaxedDefaultButNotSetNewShare($shareType): void {
1234
+        $share = $this->manager->newShare();
1235
+        $share->setShareType($shareType);
1236
+
1237
+        if ($shareType === IShare::TYPE_USER) {
1238
+            $this->config->method('getAppValue')
1239
+                ->willReturnMap([
1240
+                    ['core', 'shareapi_enforce_internal_expire_date', 'no', 'yes'],
1241
+                    ['core', 'shareapi_internal_expire_after_n_days', '7', '3'],
1242
+                    ['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
1243
+                    ['core', 'internal_defaultExpDays', '3', '1'],
1244
+                ]);
1245
+        } else {
1246
+            $this->config->method('getAppValue')
1247
+                ->willReturnMap([
1248
+                    ['core', 'shareapi_enforce_remote_expire_date', 'no', 'yes'],
1249
+                    ['core', 'shareapi_remote_expire_after_n_days', '7', '3'],
1250
+                    ['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
1251
+                    ['core', 'remote_defaultExpDays', '3', '1'],
1252
+                ]);
1253
+        }
1254
+
1255
+        $expected = new \DateTime('now', $this->timezone);
1256
+        $expected->setTime(0, 0, 0);
1257
+        $expected->add(new \DateInterval('P1D'));
1258
+
1259
+        self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1260
+
1261
+        $this->assertNotNull($share->getExpirationDate());
1262
+        $this->assertEquals($expected, $share->getExpirationDate());
1263
+    }
1264
+
1265
+    #[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1266
+    public function testValidateExpirationDateInternalEnforceTooFarIntoFuture($shareType): void {
1267
+        $this->expectException(GenericShareException::class);
1268
+        $this->expectExceptionMessage('Cannot set expiration date more than 3 days in the future');
1269
+
1270
+        $future = new \DateTime();
1271
+        $future->add(new \DateInterval('P7D'));
1272
+
1273
+        $share = $this->manager->newShare();
1274
+        $share->setShareType($shareType);
1275
+        $share->setExpirationDate($future);
1276
+
1277
+        if ($shareType === IShare::TYPE_USER) {
1278
+            $this->config->method('getAppValue')
1279
+                ->willReturnMap([
1280
+                    ['core', 'shareapi_enforce_internal_expire_date', 'no', 'yes'],
1281
+                    ['core', 'shareapi_internal_expire_after_n_days', '7', '3'],
1282
+                    ['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
1283
+                ]);
1284
+        } else {
1285
+            $this->config->method('getAppValue')
1286
+                ->willReturnMap([
1287
+                    ['core', 'shareapi_enforce_remote_expire_date', 'no', 'yes'],
1288
+                    ['core', 'shareapi_remote_expire_after_n_days', '7', '3'],
1289
+                    ['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
1290
+                ]);
1291
+        }
1292
+
1293
+        self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1294
+    }
1295
+
1296
+    #[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1297
+    public function testValidateExpirationDateInternalEnforceValid($shareType): void {
1298
+        $future = new \DateTime('now', $this->dateTimeZone->getTimeZone());
1299
+        $future->add(new \DateInterval('P2D'));
1300
+        $future->setTime(1, 2, 3);
1301
+
1302
+        $expected = clone $future;
1303
+        $expected->setTime(0, 0, 0);
1304
+
1305
+        $share = $this->manager->newShare();
1306
+        $share->setShareType($shareType);
1307
+        $share->setExpirationDate($future);
1308
+
1309
+        if ($shareType === IShare::TYPE_USER) {
1310
+            $this->config->method('getAppValue')
1311
+                ->willReturnMap([
1312
+                    ['core', 'shareapi_enforce_internal_expire_date', 'no', 'yes'],
1313
+                    ['core', 'shareapi_internal_expire_after_n_days', '7', '3'],
1314
+                    ['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
1315
+                ]);
1316
+        } else {
1317
+            $this->config->method('getAppValue')
1318
+                ->willReturnMap([
1319
+                    ['core', 'shareapi_enforce_remote_expire_date', 'no', 'yes'],
1320
+                    ['core', 'shareapi_remote_expire_after_n_days', '7', '3'],
1321
+                    ['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
1322
+                ]);
1323
+        }
1324
+
1325
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
1326
+        Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1327
+        $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($future) {
1328
+            return $data['expirationDate'] == $future;
1329
+        }));
1330
+
1331
+        self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1332
+
1333
+        $this->assertEquals($expected, $share->getExpirationDate());
1334
+    }
1335
+
1336
+    #[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1337
+    public function testValidateExpirationDateInternalNoDefault($shareType): void {
1338
+        $date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
1339
+        $date->add(new \DateInterval('P5D'));
1340
+        $date->setTime(1, 2, 3);
1341
+
1342
+        $expected = clone $date;
1343
+        $expected->setTime(0, 0, 0);
1344
+
1345
+        $share = $this->manager->newShare();
1346
+        $share->setShareType($shareType);
1347
+        $share->setExpirationDate($date);
1348
+
1349
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
1350
+        Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1351
+        $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
1352
+            return $data['expirationDate'] == $expected && $data['passwordSet'] === false;
1353
+        }));
1354
+
1355
+        self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1356
+
1357
+        $this->assertEquals($expected, $share->getExpirationDate());
1358
+    }
1359
+
1360
+    #[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1361
+    public function testValidateExpirationDateInternalNoDateNoDefault($shareType): void {
1362
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
1363
+        Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1364
+        $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) {
1365
+            return $data['expirationDate'] === null && $data['passwordSet'] === true;
1366
+        }));
1367
+
1368
+        $share = $this->manager->newShare();
1369
+        $share->setShareType($shareType);
1370
+        $share->setPassword('password');
1371
+
1372
+        self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1373
+
1374
+        $this->assertNull($share->getExpirationDate());
1375
+    }
1376
+
1377
+    #[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1378
+    public function testValidateExpirationDateInternalNoDateDefault($shareType): void {
1379
+        $share = $this->manager->newShare();
1380
+        $share->setShareType($shareType);
1381
+
1382
+        $expected = new \DateTime('now', $this->timezone);
1383
+        $expected->setTime(0, 0);
1384
+        $expected->add(new \DateInterval('P3D'));
1385
+        $expected->setTimezone(new \DateTimeZone(date_default_timezone_get()));
1386
+
1387
+        if ($shareType === IShare::TYPE_USER) {
1388
+            $this->config->method('getAppValue')
1389
+                ->willReturnMap([
1390
+                    ['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
1391
+                    ['core', 'shareapi_internal_expire_after_n_days', '7', '3'],
1392
+                    ['core', 'internal_defaultExpDays', '3', '3'],
1393
+                ]);
1394
+        } else {
1395
+            $this->config->method('getAppValue')
1396
+                ->willReturnMap([
1397
+                    ['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
1398
+                    ['core', 'shareapi_remote_expire_after_n_days', '7', '3'],
1399
+                    ['core', 'remote_defaultExpDays', '3', '3'],
1400
+                ]);
1401
+        }
1402
+
1403
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
1404
+        Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1405
+        $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
1406
+            return $data['expirationDate'] == $expected;
1407
+        }));
1408
+
1409
+        self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1410
+
1411
+        $this->assertEquals($expected, $share->getExpirationDate());
1412
+    }
1413
+
1414
+    #[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1415
+    public function testValidateExpirationDateInternalDefault($shareType): void {
1416
+        $future = new \DateTime('now', $this->timezone);
1417
+        $future->add(new \DateInterval('P5D'));
1418
+        $future->setTime(1, 2, 3);
1419
+
1420
+        $expected = clone $future;
1421
+        $expected->setTime(0, 0);
1422
+
1423
+        $share = $this->manager->newShare();
1424
+        $share->setShareType($shareType);
1425
+        $share->setExpirationDate($future);
1426
+
1427
+        if ($shareType === IShare::TYPE_USER) {
1428
+            $this->config->method('getAppValue')
1429
+                ->willReturnMap([
1430
+                    ['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
1431
+                    ['core', 'shareapi_internal_expire_after_n_days', '7', '3'],
1432
+                    ['core', 'internal_defaultExpDays', '3', '1'],
1433
+                ]);
1434
+        } else {
1435
+            $this->config->method('getAppValue')
1436
+                ->willReturnMap([
1437
+                    ['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
1438
+                    ['core', 'shareapi_remote_expire_after_n_days', '7', '3'],
1439
+                    ['core', 'remote_defaultExpDays', '3', '1'],
1440
+                ]);
1441
+        }
1442
+
1443
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
1444
+        Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1445
+        $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
1446
+            return $data['expirationDate'] == $expected;
1447
+        }));
1448
+
1449
+        self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1450
+
1451
+        $this->assertEquals($expected, $share->getExpirationDate());
1452
+    }
1453
+
1454
+    #[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1455
+    public function testValidateExpirationDateInternalHookModification($shareType): void {
1456
+        $nextWeek = new \DateTime('now', $this->timezone);
1457
+        $nextWeek->add(new \DateInterval('P7D'));
1458
+        $nextWeek->setTime(0, 0, 0);
1459
+
1460
+        $save = clone $nextWeek;
1461
+
1462
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
1463
+        Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1464
+        $hookListener->expects($this->once())->method('listener')->willReturnCallback(function ($data): void {
1465
+            $data['expirationDate']->sub(new \DateInterval('P2D'));
1466
+        });
1467
+
1468
+        $share = $this->manager->newShare();
1469
+        $share->setShareType($shareType);
1470
+        $share->setExpirationDate($nextWeek);
1471
+
1472
+        self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1473
+
1474
+        $save->sub(new \DateInterval('P2D'));
1475
+        $this->assertEquals($save, $share->getExpirationDate());
1476
+    }
1477
+
1478
+    #[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1479
+    public function testValidateExpirationDateInternalHookException($shareType): void {
1480
+        $this->expectException(\Exception::class);
1481
+        $this->expectExceptionMessage('Invalid date!');
1482
+
1483
+        $nextWeek = new \DateTime();
1484
+        $nextWeek->add(new \DateInterval('P7D'));
1485
+        $nextWeek->setTime(0, 0, 0);
1486
+
1487
+        $share = $this->manager->newShare();
1488
+        $share->setShareType($shareType);
1489
+        $share->setExpirationDate($nextWeek);
1490
+
1491
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
1492
+        Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1493
+        $hookListener->expects($this->once())->method('listener')->willReturnCallback(function ($data): void {
1494
+            $data['accepted'] = false;
1495
+            $data['message'] = 'Invalid date!';
1496
+        });
1497
+
1498
+        self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1499
+    }
1500
+
1501
+    #[\PHPUnit\Framework\Attributes\DataProvider('validateExpirationDateInternalProvider')]
1502
+    public function testValidateExpirationDateInternalExistingShareNoDefault($shareType): void {
1503
+        $share = $this->manager->newShare();
1504
+        $share->setShareType($shareType);
1505
+        $share->setId('42')->setProviderId('foo');
1506
+
1507
+        if ($shareType === IShare::TYPE_USER) {
1508
+            $this->config->method('getAppValue')
1509
+                ->willReturnMap([
1510
+                    ['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
1511
+                    ['core', 'shareapi_internal_expire_after_n_days', '7', '6'],
1512
+                ]);
1513
+        } else {
1514
+            $this->config->method('getAppValue')
1515
+                ->willReturnMap([
1516
+                    ['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
1517
+                    ['core', 'shareapi_remote_expire_after_n_days', '7', '6'],
1518
+                ]);
1519
+        }
1520
+
1521
+        self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
1522
+
1523
+        $this->assertEquals(null, $share->getExpirationDate());
1524
+    }
1525
+
1526
+    public function testValidateExpirationDateInPast(): void {
1527
+        $this->expectException(GenericShareException::class);
1528
+        $this->expectExceptionMessage('Expiration date is in the past');
1529
+
1530
+        // Expire date in the past
1531
+        $past = new \DateTime();
1532
+        $past->sub(new \DateInterval('P1D'));
1533
+
1534
+        $share = $this->manager->newShare();
1535
+        $share->setExpirationDate($past);
1536
+
1537
+        self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1538
+    }
1539
+
1540
+    public function testValidateExpirationDateEnforceButNotSet(): void {
1541
+        $this->expectException(\InvalidArgumentException::class);
1542
+        $this->expectExceptionMessage('Expiration date is enforced');
1543
+
1544
+        $share = $this->manager->newShare();
1545
+        $share->setProviderId('foo')->setId('bar');
1546
+
1547
+        $this->appConfig->method('getValueBool')
1548
+            ->willReturnMap([
1549
+                ['core', 'shareapi_default_expire_date', true],
1550
+                ['core', 'shareapi_enforce_expire_date', true],
1551
+            ]);
1552
+
1553
+        self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1554
+    }
1555
+
1556
+    public function testValidateExpirationDateEnforceButNotEnabledAndNotSet(): void {
1557
+        $share = $this->manager->newShare();
1558
+        $share->setProviderId('foo')->setId('bar');
1559
+
1560
+        $this->config->method('getAppValue')
1561
+            ->willReturnMap([
1562
+                ['core', 'shareapi_enforce_expire_date', 'no', 'yes'],
1563
+            ]);
1564
+
1565
+        self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1566
+
1567
+        $this->assertNull($share->getExpirationDate());
1568
+    }
1569
+
1570
+    public function testValidateExpirationDateEnforceButNotSetNewShare(): void {
1571
+        $share = $this->manager->newShare();
1572
+
1573
+        $this->config->method('getAppValue')
1574
+            ->willReturnMap([
1575
+                ['core', 'shareapi_expire_after_n_days', '7', '3'],
1576
+                ['core', 'link_defaultExpDays', '3', '3'],
1577
+            ]);
1578
+
1579
+        $this->appConfig->method('getValueBool')
1580
+            ->willReturnMap([
1581
+                ['core', 'shareapi_default_expire_date', true],
1582
+                ['core', 'shareapi_enforce_expire_date', true],
1583
+            ]);
1584
+
1585
+        $expected = new \DateTime('now', $this->timezone);
1586
+        $expected->setTime(0, 0, 0);
1587
+        $expected->add(new \DateInterval('P3D'));
1588
+
1589
+        self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1590
+
1591
+        $this->assertNotNull($share->getExpirationDate());
1592
+        $this->assertEquals($expected, $share->getExpirationDate());
1593
+    }
1594
+
1595
+    public function testValidateExpirationDateEnforceRelaxedDefaultButNotSetNewShare(): void {
1596
+        $share = $this->manager->newShare();
1597
+
1598
+        $this->config->method('getAppValue')
1599
+            ->willReturnMap([
1600
+                ['core', 'shareapi_expire_after_n_days', '7', '3'],
1601
+                ['core', 'link_defaultExpDays', '3', '1'],
1602
+            ]);
1603
+
1604
+        $this->appConfig->method('getValueBool')
1605
+            ->willReturnMap([
1606
+                ['core', 'shareapi_default_expire_date', true],
1607
+                ['core', 'shareapi_enforce_expire_date', true],
1608
+            ]);
1609
+
1610
+        $expected = new \DateTime('now', $this->timezone);
1611
+        $expected->setTime(0, 0, 0);
1612
+        $expected->add(new \DateInterval('P1D'));
1613 1613
 
1614
-		self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1614
+        self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1615 1615
 
1616
-		$this->assertNotNull($share->getExpirationDate());
1617
-		$this->assertEquals($expected, $share->getExpirationDate());
1618
-	}
1616
+        $this->assertNotNull($share->getExpirationDate());
1617
+        $this->assertEquals($expected, $share->getExpirationDate());
1618
+    }
1619 1619
 
1620
-	public function testValidateExpirationDateEnforceTooFarIntoFuture(): void {
1621
-		$this->expectException(GenericShareException::class);
1622
-		$this->expectExceptionMessage('Cannot set expiration date more than 3 days in the future');
1620
+    public function testValidateExpirationDateEnforceTooFarIntoFuture(): void {
1621
+        $this->expectException(GenericShareException::class);
1622
+        $this->expectExceptionMessage('Cannot set expiration date more than 3 days in the future');
1623 1623
 
1624
-		$future = new \DateTime();
1625
-		$future->add(new \DateInterval('P7D'));
1624
+        $future = new \DateTime();
1625
+        $future->add(new \DateInterval('P7D'));
1626 1626
 
1627
-		$share = $this->manager->newShare();
1628
-		$share->setExpirationDate($future);
1627
+        $share = $this->manager->newShare();
1628
+        $share->setExpirationDate($future);
1629 1629
 
1630
-		$this->config->method('getAppValue')
1631
-			->willReturnMap([
1632
-				['core', 'shareapi_expire_after_n_days', '7', '3'],
1633
-			]);
1630
+        $this->config->method('getAppValue')
1631
+            ->willReturnMap([
1632
+                ['core', 'shareapi_expire_after_n_days', '7', '3'],
1633
+            ]);
1634 1634
 
1635
-		$this->appConfig->method('getValueBool')
1636
-			->willReturnMap([
1637
-				['core', 'shareapi_default_expire_date', true],
1638
-				['core', 'shareapi_enforce_expire_date', true],
1639
-			]);
1640
-
1641
-		self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1642
-	}
1635
+        $this->appConfig->method('getValueBool')
1636
+            ->willReturnMap([
1637
+                ['core', 'shareapi_default_expire_date', true],
1638
+                ['core', 'shareapi_enforce_expire_date', true],
1639
+            ]);
1640
+
1641
+        self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1642
+    }
1643 1643
 
1644
-	public function testValidateExpirationDateEnforceValid(): void {
1645
-		$future = new \DateTime('now', $this->timezone);
1646
-		$future->add(new \DateInterval('P2D'));
1647
-		$future->setTime(1, 2, 3);
1648
-
1649
-		$expected = clone $future;
1650
-		$expected->setTime(0, 0, 0);
1644
+    public function testValidateExpirationDateEnforceValid(): void {
1645
+        $future = new \DateTime('now', $this->timezone);
1646
+        $future->add(new \DateInterval('P2D'));
1647
+        $future->setTime(1, 2, 3);
1648
+
1649
+        $expected = clone $future;
1650
+        $expected->setTime(0, 0, 0);
1651 1651
 
1652
-		$share = $this->manager->newShare();
1653
-		$share->setExpirationDate($future);
1652
+        $share = $this->manager->newShare();
1653
+        $share->setExpirationDate($future);
1654 1654
 
1655
-		$this->config->method('getAppValue')
1656
-			->willReturnMap([
1657
-				['core', 'shareapi_expire_after_n_days', '7', '3'],
1658
-			]);
1655
+        $this->config->method('getAppValue')
1656
+            ->willReturnMap([
1657
+                ['core', 'shareapi_expire_after_n_days', '7', '3'],
1658
+            ]);
1659 1659
 
1660
-		$this->appConfig->method('getValueBool')
1661
-			->willReturnMap([
1662
-				['core', 'shareapi_default_expire_date', true],
1663
-				['core', 'shareapi_enforce_expire_date', true],
1664
-			]);
1665
-
1666
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
1667
-		Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1668
-		$hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($future) {
1669
-			return $data['expirationDate'] == $future;
1670
-		}));
1671
-
1672
-		self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1673
-
1674
-		$this->assertEquals($expected, $share->getExpirationDate());
1675
-	}
1676
-
1677
-	public function testValidateExpirationDateNoDefault(): void {
1678
-		$date = new \DateTime('now', $this->timezone);
1679
-		$date->add(new \DateInterval('P5D'));
1680
-		$date->setTime(1, 2, 3);
1681
-
1682
-		$expected = clone $date;
1683
-		$expected->setTime(0, 0);
1684
-		$expected->setTimezone(new \DateTimeZone(date_default_timezone_get()));
1685
-
1686
-		$share = $this->manager->newShare();
1687
-		$share->setExpirationDate($date);
1688
-
1689
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
1690
-		Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1691
-		$hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
1692
-			return $data['expirationDate'] == $expected && $data['passwordSet'] === false;
1693
-		}));
1694
-
1695
-		self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1696
-
1697
-		$this->assertEquals($expected, $share->getExpirationDate());
1698
-	}
1699
-
1700
-	public function testValidateExpirationDateNoDateNoDefault(): void {
1701
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
1702
-		Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1703
-		$hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) {
1704
-			return $data['expirationDate'] === null && $data['passwordSet'] === true;
1705
-		}));
1706
-
1707
-		$share = $this->manager->newShare();
1708
-		$share->setPassword('password');
1709
-
1710
-		self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1711
-
1712
-		$this->assertNull($share->getExpirationDate());
1713
-	}
1714
-
1715
-	public function testValidateExpirationDateNoDateDefault(): void {
1716
-		$share = $this->manager->newShare();
1717
-
1718
-		$expected = new \DateTime('now', $this->timezone);
1719
-		$expected->add(new \DateInterval('P3D'));
1720
-		$expected->setTime(0, 0);
1721
-		$expected->setTimezone(new \DateTimeZone(date_default_timezone_get()));
1722
-
1723
-		$this->config->method('getAppValue')
1724
-			->willReturnMap([
1725
-				['core', 'shareapi_expire_after_n_days', '7', '3'],
1726
-				['core', 'link_defaultExpDays', '3', '3'],
1727
-			]);
1728
-
1729
-		$this->appConfig->method('getValueBool')
1730
-			->willReturnMap([
1731
-				['core', 'shareapi_default_expire_date', true],
1732
-				['core', 'shareapi_enforce_expire_date', false],
1733
-			]);
1734
-
1735
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
1736
-		Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1737
-		$hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
1738
-			return $data['expirationDate'] == $expected;
1739
-		}));
1740
-
1741
-		self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1742
-
1743
-		$this->assertEquals($expected, $share->getExpirationDate());
1744
-	}
1745
-
1746
-	public function testValidateExpirationDateDefault(): void {
1747
-		$future = new \DateTime('now', $this->timezone);
1748
-		$future->add(new \DateInterval('P5D'));
1749
-		$future->setTime(1, 2, 3);
1750
-
1751
-		$expected = clone $future;
1752
-		$expected->setTime(0, 0);
1753
-		$expected->setTimezone(new \DateTimeZone(date_default_timezone_get()));
1754
-
1755
-		$share = $this->manager->newShare();
1756
-		$share->setExpirationDate($future);
1757
-
1758
-		$this->config->method('getAppValue')
1759
-			->willReturnMap([
1760
-				['core', 'shareapi_expire_after_n_days', '7', '3'],
1761
-				['core', 'link_defaultExpDays', '3', '1'],
1762
-			]);
1763
-
1764
-		$this->appConfig->method('getValueBool')
1765
-			->willReturnMap([
1766
-				['core', 'shareapi_default_expire_date', true],
1767
-				['core', 'shareapi_enforce_expire_date', false],
1768
-			]);
1769
-
1770
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
1771
-		Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1772
-		$hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
1773
-			return $data['expirationDate'] == $expected;
1774
-		}));
1775
-
1776
-		self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1777
-
1778
-		$this->assertEquals($expected, $share->getExpirationDate());
1779
-	}
1780
-
1781
-	public function testValidateExpirationNegativeOffsetTimezone(): void {
1782
-		$this->timezone = new \DateTimeZone('Pacific/Tahiti');
1783
-		$future = new \DateTime();
1784
-		$future->add(new \DateInterval('P5D'));
1785
-
1786
-		$expected = clone $future;
1787
-		$expected->setTimezone($this->timezone);
1788
-		$expected->setTime(0, 0);
1789
-		$expected->setTimezone(new \DateTimeZone(date_default_timezone_get()));
1790
-
1791
-		$share = $this->manager->newShare();
1792
-		$share->setExpirationDate($future);
1793
-
1794
-		$this->config->method('getAppValue')
1795
-			->willReturnMap([
1796
-				['core', 'shareapi_expire_after_n_days', '7', '3'],
1797
-				['core', 'link_defaultExpDays', '3', '1'],
1798
-			]);
1799
-
1800
-		$this->appConfig->method('getValueBool')
1801
-			->willReturnMap([
1802
-				['core', 'shareapi_default_expire_date', true],
1803
-				['core', 'shareapi_enforce_expire_date', false],
1804
-			]);
1805
-
1806
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
1807
-		Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1808
-		$hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
1809
-			return $data['expirationDate'] == $expected;
1810
-		}));
1811
-
1812
-		self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1813
-
1814
-		$this->assertEquals($expected, $share->getExpirationDate());
1815
-	}
1816
-
1817
-	public function testValidateExpirationDateHookModification(): void {
1818
-		$nextWeek = new \DateTime('now', $this->timezone);
1819
-		$nextWeek->add(new \DateInterval('P7D'));
1820
-
1821
-		$save = clone $nextWeek;
1822
-		$save->setTime(0, 0);
1823
-		$save->sub(new \DateInterval('P2D'));
1824
-		$save->setTimezone(new \DateTimeZone(date_default_timezone_get()));
1825
-
1826
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
1827
-		Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1828
-		$hookListener->expects($this->once())->method('listener')->willReturnCallback(function ($data): void {
1829
-			$data['expirationDate']->sub(new \DateInterval('P2D'));
1830
-		});
1831
-
1832
-		$share = $this->manager->newShare();
1833
-		$share->setExpirationDate($nextWeek);
1834
-
1835
-		self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1836
-
1837
-		$this->assertEquals($save, $share->getExpirationDate());
1838
-	}
1839
-
1840
-	public function testValidateExpirationDateHookException(): void {
1841
-		$this->expectException(\Exception::class);
1842
-		$this->expectExceptionMessage('Invalid date!');
1843
-
1844
-		$nextWeek = new \DateTime();
1845
-		$nextWeek->add(new \DateInterval('P7D'));
1846
-		$nextWeek->setTime(0, 0, 0);
1847
-
1848
-		$share = $this->manager->newShare();
1849
-		$share->setExpirationDate($nextWeek);
1850
-
1851
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
1852
-		Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1853
-		$hookListener->expects($this->once())->method('listener')->willReturnCallback(function ($data): void {
1854
-			$data['accepted'] = false;
1855
-			$data['message'] = 'Invalid date!';
1856
-		});
1857
-
1858
-		self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1859
-	}
1860
-
1861
-	public function testValidateExpirationDateExistingShareNoDefault(): void {
1862
-		$share = $this->manager->newShare();
1863
-
1864
-		$share->setId('42')->setProviderId('foo');
1865
-
1866
-		$this->config->method('getAppValue')
1867
-			->willReturnMap([
1868
-				['core', 'shareapi_expire_after_n_days', '7', '6'],
1869
-			]);
1870
-
1871
-		$this->appConfig->method('getValueBool')
1872
-			->willReturnMap([
1873
-				['core', 'shareapi_default_expire_date', true],
1874
-				['core', 'shareapi_enforce_expire_date', false],
1875
-			]);
1876
-		self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1877
-
1878
-		$this->assertEquals(null, $share->getExpirationDate());
1879
-	}
1880
-
1881
-	public function testUserCreateChecksShareWithGroupMembersOnlyDifferentGroups(): void {
1882
-		$this->expectException(\Exception::class);
1883
-		$this->expectExceptionMessage('Sharing is only allowed with group members');
1884
-
1885
-		$share = $this->manager->newShare();
1886
-
1887
-		$sharedBy = $this->createMock(IUser::class);
1888
-		$sharedWith = $this->createMock(IUser::class);
1889
-		$share->setSharedBy('sharedBy')->setSharedWith('sharedWith');
1660
+        $this->appConfig->method('getValueBool')
1661
+            ->willReturnMap([
1662
+                ['core', 'shareapi_default_expire_date', true],
1663
+                ['core', 'shareapi_enforce_expire_date', true],
1664
+            ]);
1665
+
1666
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
1667
+        Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1668
+        $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($future) {
1669
+            return $data['expirationDate'] == $future;
1670
+        }));
1671
+
1672
+        self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1673
+
1674
+        $this->assertEquals($expected, $share->getExpirationDate());
1675
+    }
1676
+
1677
+    public function testValidateExpirationDateNoDefault(): void {
1678
+        $date = new \DateTime('now', $this->timezone);
1679
+        $date->add(new \DateInterval('P5D'));
1680
+        $date->setTime(1, 2, 3);
1681
+
1682
+        $expected = clone $date;
1683
+        $expected->setTime(0, 0);
1684
+        $expected->setTimezone(new \DateTimeZone(date_default_timezone_get()));
1685
+
1686
+        $share = $this->manager->newShare();
1687
+        $share->setExpirationDate($date);
1688
+
1689
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
1690
+        Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1691
+        $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
1692
+            return $data['expirationDate'] == $expected && $data['passwordSet'] === false;
1693
+        }));
1694
+
1695
+        self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1696
+
1697
+        $this->assertEquals($expected, $share->getExpirationDate());
1698
+    }
1699
+
1700
+    public function testValidateExpirationDateNoDateNoDefault(): void {
1701
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
1702
+        Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1703
+        $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) {
1704
+            return $data['expirationDate'] === null && $data['passwordSet'] === true;
1705
+        }));
1706
+
1707
+        $share = $this->manager->newShare();
1708
+        $share->setPassword('password');
1709
+
1710
+        self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1711
+
1712
+        $this->assertNull($share->getExpirationDate());
1713
+    }
1714
+
1715
+    public function testValidateExpirationDateNoDateDefault(): void {
1716
+        $share = $this->manager->newShare();
1717
+
1718
+        $expected = new \DateTime('now', $this->timezone);
1719
+        $expected->add(new \DateInterval('P3D'));
1720
+        $expected->setTime(0, 0);
1721
+        $expected->setTimezone(new \DateTimeZone(date_default_timezone_get()));
1722
+
1723
+        $this->config->method('getAppValue')
1724
+            ->willReturnMap([
1725
+                ['core', 'shareapi_expire_after_n_days', '7', '3'],
1726
+                ['core', 'link_defaultExpDays', '3', '3'],
1727
+            ]);
1728
+
1729
+        $this->appConfig->method('getValueBool')
1730
+            ->willReturnMap([
1731
+                ['core', 'shareapi_default_expire_date', true],
1732
+                ['core', 'shareapi_enforce_expire_date', false],
1733
+            ]);
1734
+
1735
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
1736
+        Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1737
+        $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
1738
+            return $data['expirationDate'] == $expected;
1739
+        }));
1740
+
1741
+        self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1742
+
1743
+        $this->assertEquals($expected, $share->getExpirationDate());
1744
+    }
1745
+
1746
+    public function testValidateExpirationDateDefault(): void {
1747
+        $future = new \DateTime('now', $this->timezone);
1748
+        $future->add(new \DateInterval('P5D'));
1749
+        $future->setTime(1, 2, 3);
1750
+
1751
+        $expected = clone $future;
1752
+        $expected->setTime(0, 0);
1753
+        $expected->setTimezone(new \DateTimeZone(date_default_timezone_get()));
1754
+
1755
+        $share = $this->manager->newShare();
1756
+        $share->setExpirationDate($future);
1757
+
1758
+        $this->config->method('getAppValue')
1759
+            ->willReturnMap([
1760
+                ['core', 'shareapi_expire_after_n_days', '7', '3'],
1761
+                ['core', 'link_defaultExpDays', '3', '1'],
1762
+            ]);
1763
+
1764
+        $this->appConfig->method('getValueBool')
1765
+            ->willReturnMap([
1766
+                ['core', 'shareapi_default_expire_date', true],
1767
+                ['core', 'shareapi_enforce_expire_date', false],
1768
+            ]);
1769
+
1770
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
1771
+        Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1772
+        $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
1773
+            return $data['expirationDate'] == $expected;
1774
+        }));
1775
+
1776
+        self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1777
+
1778
+        $this->assertEquals($expected, $share->getExpirationDate());
1779
+    }
1780
+
1781
+    public function testValidateExpirationNegativeOffsetTimezone(): void {
1782
+        $this->timezone = new \DateTimeZone('Pacific/Tahiti');
1783
+        $future = new \DateTime();
1784
+        $future->add(new \DateInterval('P5D'));
1785
+
1786
+        $expected = clone $future;
1787
+        $expected->setTimezone($this->timezone);
1788
+        $expected->setTime(0, 0);
1789
+        $expected->setTimezone(new \DateTimeZone(date_default_timezone_get()));
1790
+
1791
+        $share = $this->manager->newShare();
1792
+        $share->setExpirationDate($future);
1793
+
1794
+        $this->config->method('getAppValue')
1795
+            ->willReturnMap([
1796
+                ['core', 'shareapi_expire_after_n_days', '7', '3'],
1797
+                ['core', 'link_defaultExpDays', '3', '1'],
1798
+            ]);
1799
+
1800
+        $this->appConfig->method('getValueBool')
1801
+            ->willReturnMap([
1802
+                ['core', 'shareapi_default_expire_date', true],
1803
+                ['core', 'shareapi_enforce_expire_date', false],
1804
+            ]);
1805
+
1806
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
1807
+        Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1808
+        $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
1809
+            return $data['expirationDate'] == $expected;
1810
+        }));
1811
+
1812
+        self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1813
+
1814
+        $this->assertEquals($expected, $share->getExpirationDate());
1815
+    }
1816
+
1817
+    public function testValidateExpirationDateHookModification(): void {
1818
+        $nextWeek = new \DateTime('now', $this->timezone);
1819
+        $nextWeek->add(new \DateInterval('P7D'));
1820
+
1821
+        $save = clone $nextWeek;
1822
+        $save->setTime(0, 0);
1823
+        $save->sub(new \DateInterval('P2D'));
1824
+        $save->setTimezone(new \DateTimeZone(date_default_timezone_get()));
1825
+
1826
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
1827
+        Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1828
+        $hookListener->expects($this->once())->method('listener')->willReturnCallback(function ($data): void {
1829
+            $data['expirationDate']->sub(new \DateInterval('P2D'));
1830
+        });
1831
+
1832
+        $share = $this->manager->newShare();
1833
+        $share->setExpirationDate($nextWeek);
1834
+
1835
+        self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1836
+
1837
+        $this->assertEquals($save, $share->getExpirationDate());
1838
+    }
1839
+
1840
+    public function testValidateExpirationDateHookException(): void {
1841
+        $this->expectException(\Exception::class);
1842
+        $this->expectExceptionMessage('Invalid date!');
1843
+
1844
+        $nextWeek = new \DateTime();
1845
+        $nextWeek->add(new \DateInterval('P7D'));
1846
+        $nextWeek->setTime(0, 0, 0);
1847
+
1848
+        $share = $this->manager->newShare();
1849
+        $share->setExpirationDate($nextWeek);
1850
+
1851
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
1852
+        Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
1853
+        $hookListener->expects($this->once())->method('listener')->willReturnCallback(function ($data): void {
1854
+            $data['accepted'] = false;
1855
+            $data['message'] = 'Invalid date!';
1856
+        });
1857
+
1858
+        self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1859
+    }
1860
+
1861
+    public function testValidateExpirationDateExistingShareNoDefault(): void {
1862
+        $share = $this->manager->newShare();
1863
+
1864
+        $share->setId('42')->setProviderId('foo');
1865
+
1866
+        $this->config->method('getAppValue')
1867
+            ->willReturnMap([
1868
+                ['core', 'shareapi_expire_after_n_days', '7', '6'],
1869
+            ]);
1870
+
1871
+        $this->appConfig->method('getValueBool')
1872
+            ->willReturnMap([
1873
+                ['core', 'shareapi_default_expire_date', true],
1874
+                ['core', 'shareapi_enforce_expire_date', false],
1875
+            ]);
1876
+        self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
1877
+
1878
+        $this->assertEquals(null, $share->getExpirationDate());
1879
+    }
1880
+
1881
+    public function testUserCreateChecksShareWithGroupMembersOnlyDifferentGroups(): void {
1882
+        $this->expectException(\Exception::class);
1883
+        $this->expectExceptionMessage('Sharing is only allowed with group members');
1884
+
1885
+        $share = $this->manager->newShare();
1886
+
1887
+        $sharedBy = $this->createMock(IUser::class);
1888
+        $sharedWith = $this->createMock(IUser::class);
1889
+        $share->setSharedBy('sharedBy')->setSharedWith('sharedWith');
1890 1890
 
1891
-		$this->groupManager
1892
-			->method('getUserGroupIds')
1893
-			->willReturnMap(
1894
-				[
1895
-					[$sharedBy, ['group1']],
1896
-					[$sharedWith, ['group2']],
1897
-				]
1898
-			);
1891
+        $this->groupManager
1892
+            ->method('getUserGroupIds')
1893
+            ->willReturnMap(
1894
+                [
1895
+                    [$sharedBy, ['group1']],
1896
+                    [$sharedWith, ['group2']],
1897
+                ]
1898
+            );
1899 1899
 
1900
-		$this->userManager->method('get')->willReturnMap([
1901
-			['sharedBy', $sharedBy],
1902
-			['sharedWith', $sharedWith],
1903
-		]);
1900
+        $this->userManager->method('get')->willReturnMap([
1901
+            ['sharedBy', $sharedBy],
1902
+            ['sharedWith', $sharedWith],
1903
+        ]);
1904 1904
 
1905
-		$this->config
1906
-			->method('getAppValue')
1907
-			->willReturnMap([
1908
-				['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
1909
-				['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
1910
-			]);
1905
+        $this->config
1906
+            ->method('getAppValue')
1907
+            ->willReturnMap([
1908
+                ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
1909
+                ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
1910
+            ]);
1911 1911
 
1912
-		self::invokePrivate($this->manager, 'userCreateChecks', [$share]);
1913
-	}
1914
-
1915
-	public function testUserCreateChecksShareWithGroupMembersOnlySharedGroup(): void {
1916
-		$share = $this->manager->newShare();
1917
-
1918
-		$sharedBy = $this->createMock(IUser::class);
1919
-		$sharedWith = $this->createMock(IUser::class);
1920
-		$share->setSharedBy('sharedBy')->setSharedWith('sharedWith');
1921
-
1922
-		$path = $this->createMock(Node::class);
1923
-		$share->setNode($path);
1912
+        self::invokePrivate($this->manager, 'userCreateChecks', [$share]);
1913
+    }
1914
+
1915
+    public function testUserCreateChecksShareWithGroupMembersOnlySharedGroup(): void {
1916
+        $share = $this->manager->newShare();
1917
+
1918
+        $sharedBy = $this->createMock(IUser::class);
1919
+        $sharedWith = $this->createMock(IUser::class);
1920
+        $share->setSharedBy('sharedBy')->setSharedWith('sharedWith');
1921
+
1922
+        $path = $this->createMock(Node::class);
1923
+        $share->setNode($path);
1924 1924
 
1925
-		$this->groupManager
1926
-			->method('getUserGroupIds')
1927
-			->willReturnMap(
1928
-				[
1929
-					[$sharedBy, ['group1', 'group3']],
1930
-					[$sharedWith, ['group2', 'group3']],
1931
-				]
1932
-			);
1925
+        $this->groupManager
1926
+            ->method('getUserGroupIds')
1927
+            ->willReturnMap(
1928
+                [
1929
+                    [$sharedBy, ['group1', 'group3']],
1930
+                    [$sharedWith, ['group2', 'group3']],
1931
+                ]
1932
+            );
1933 1933
 
1934
-		$this->userManager->method('get')->willReturnMap([
1935
-			['sharedBy', $sharedBy],
1936
-			['sharedWith', $sharedWith],
1937
-		]);
1934
+        $this->userManager->method('get')->willReturnMap([
1935
+            ['sharedBy', $sharedBy],
1936
+            ['sharedWith', $sharedWith],
1937
+        ]);
1938 1938
 
1939
-		$this->config
1940
-			->method('getAppValue')
1941
-			->willReturnMap([
1942
-				['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
1943
-				['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
1944
-			]);
1939
+        $this->config
1940
+            ->method('getAppValue')
1941
+            ->willReturnMap([
1942
+                ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
1943
+                ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
1944
+            ]);
1945 1945
 
1946
-		$this->defaultProvider
1947
-			->method('getSharesByPath')
1948
-			->with($path)
1949
-			->willReturn([]);
1946
+        $this->defaultProvider
1947
+            ->method('getSharesByPath')
1948
+            ->with($path)
1949
+            ->willReturn([]);
1950 1950
 
1951
-		self::invokePrivate($this->manager, 'userCreateChecks', [$share]);
1952
-		$this->addToAssertionCount(1);
1953
-	}
1951
+        self::invokePrivate($this->manager, 'userCreateChecks', [$share]);
1952
+        $this->addToAssertionCount(1);
1953
+    }
1954 1954
 
1955 1955
 
1956
-	public function testUserCreateChecksIdenticalShareExists(): void {
1957
-		$this->expectException(AlreadySharedException::class);
1958
-		$this->expectExceptionMessage('Sharing name.txt failed, because this item is already shared with the account user');
1956
+    public function testUserCreateChecksIdenticalShareExists(): void {
1957
+        $this->expectException(AlreadySharedException::class);
1958
+        $this->expectExceptionMessage('Sharing name.txt failed, because this item is already shared with the account user');
1959 1959
 
1960
-		$share = $this->manager->newShare();
1961
-		$share->setSharedWithDisplayName('user');
1962
-		$share2 = $this->manager->newShare();
1960
+        $share = $this->manager->newShare();
1961
+        $share->setSharedWithDisplayName('user');
1962
+        $share2 = $this->manager->newShare();
1963 1963
 
1964
-		$sharedWith = $this->createMock(IUser::class);
1965
-		$path = $this->createMock(Node::class);
1966
-
1967
-		$share->setSharedWith('sharedWith')->setNode($path)
1968
-			->setProviderId('foo')->setId('bar');
1969
-
1970
-		$share2->setSharedWith('sharedWith')->setNode($path)
1971
-			->setProviderId('foo')->setId('baz');
1972
-
1973
-		$this->defaultProvider
1974
-			->method('getSharesByPath')
1975
-			->with($path)
1976
-			->willReturn([$share2]);
1964
+        $sharedWith = $this->createMock(IUser::class);
1965
+        $path = $this->createMock(Node::class);
1966
+
1967
+        $share->setSharedWith('sharedWith')->setNode($path)
1968
+            ->setProviderId('foo')->setId('bar');
1969
+
1970
+        $share2->setSharedWith('sharedWith')->setNode($path)
1971
+            ->setProviderId('foo')->setId('baz');
1972
+
1973
+        $this->defaultProvider
1974
+            ->method('getSharesByPath')
1975
+            ->with($path)
1976
+            ->willReturn([$share2]);
1977 1977
 
1978
-		$path->method('getName')
1979
-			->willReturn('name.txt');
1978
+        $path->method('getName')
1979
+            ->willReturn('name.txt');
1980 1980
 
1981
-		self::invokePrivate($this->manager, 'userCreateChecks', [$share]);
1982
-	}
1983
-
1984
-
1985
-	public function testUserCreateChecksIdenticalPathSharedViaGroup(): void {
1986
-		$this->expectException(AlreadySharedException::class);
1987
-		$this->expectExceptionMessage('Sharing name2.txt failed, because this item is already shared with the account userName');
1988
-
1989
-		$share = $this->manager->newShare();
1981
+        self::invokePrivate($this->manager, 'userCreateChecks', [$share]);
1982
+    }
1983
+
1984
+
1985
+    public function testUserCreateChecksIdenticalPathSharedViaGroup(): void {
1986
+        $this->expectException(AlreadySharedException::class);
1987
+        $this->expectExceptionMessage('Sharing name2.txt failed, because this item is already shared with the account userName');
1988
+
1989
+        $share = $this->manager->newShare();
1990 1990
 
1991
-		$sharedWith = $this->createMock(IUser::class);
1992
-		$sharedWith->method('getUID')->willReturn('sharedWith');
1993
-
1994
-		$this->userManager->method('get')->with('sharedWith')->willReturn($sharedWith);
1991
+        $sharedWith = $this->createMock(IUser::class);
1992
+        $sharedWith->method('getUID')->willReturn('sharedWith');
1993
+
1994
+        $this->userManager->method('get')->with('sharedWith')->willReturn($sharedWith);
1995 1995
 
1996
-		$path = $this->createMock(Node::class);
1996
+        $path = $this->createMock(Node::class);
1997 1997
 
1998
-		$share->setSharedWith('sharedWith')
1999
-			->setNode($path)
2000
-			->setShareOwner('shareOwner')
2001
-			->setSharedWithDisplayName('userName')
2002
-			->setProviderId('foo')
2003
-			->setId('bar');
1998
+        $share->setSharedWith('sharedWith')
1999
+            ->setNode($path)
2000
+            ->setShareOwner('shareOwner')
2001
+            ->setSharedWithDisplayName('userName')
2002
+            ->setProviderId('foo')
2003
+            ->setId('bar');
2004 2004
 
2005
-		$share2 = $this->manager->newShare();
2006
-		$share2->setShareType(IShare::TYPE_GROUP)
2007
-			->setShareOwner('shareOwner2')
2008
-			->setProviderId('foo')
2009
-			->setId('baz')
2010
-			->setSharedWith('group');
2005
+        $share2 = $this->manager->newShare();
2006
+        $share2->setShareType(IShare::TYPE_GROUP)
2007
+            ->setShareOwner('shareOwner2')
2008
+            ->setProviderId('foo')
2009
+            ->setId('baz')
2010
+            ->setSharedWith('group');
2011 2011
 
2012
-		$group = $this->createMock(IGroup::class);
2013
-		$group->method('inGroup')
2014
-			->with($sharedWith)
2015
-			->willReturn(true);
2012
+        $group = $this->createMock(IGroup::class);
2013
+        $group->method('inGroup')
2014
+            ->with($sharedWith)
2015
+            ->willReturn(true);
2016 2016
 
2017
-		$this->groupManager->method('get')->with('group')->willReturn($group);
2017
+        $this->groupManager->method('get')->with('group')->willReturn($group);
2018 2018
 
2019
-		$this->defaultProvider
2020
-			->method('getSharesByPath')
2021
-			->with($path)
2022
-			->willReturn([$share2]);
2019
+        $this->defaultProvider
2020
+            ->method('getSharesByPath')
2021
+            ->with($path)
2022
+            ->willReturn([$share2]);
2023 2023
 
2024
-		$path->method('getName')
2025
-			->willReturn('name2.txt');
2024
+        $path->method('getName')
2025
+            ->willReturn('name2.txt');
2026 2026
 
2027
-		self::invokePrivate($this->manager, 'userCreateChecks', [$share]);
2028
-	}
2027
+        self::invokePrivate($this->manager, 'userCreateChecks', [$share]);
2028
+    }
2029 2029
 
2030
-	public function testUserCreateChecksIdenticalPathSharedViaDeletedGroup(): void {
2031
-		$share = $this->manager->newShare();
2030
+    public function testUserCreateChecksIdenticalPathSharedViaDeletedGroup(): void {
2031
+        $share = $this->manager->newShare();
2032 2032
 
2033
-		$sharedWith = $this->createMock(IUser::class);
2034
-		$sharedWith->method('getUID')->willReturn('sharedWith');
2033
+        $sharedWith = $this->createMock(IUser::class);
2034
+        $sharedWith->method('getUID')->willReturn('sharedWith');
2035 2035
 
2036
-		$this->userManager->method('get')->with('sharedWith')->willReturn($sharedWith);
2036
+        $this->userManager->method('get')->with('sharedWith')->willReturn($sharedWith);
2037 2037
 
2038
-		$path = $this->createMock(Node::class);
2038
+        $path = $this->createMock(Node::class);
2039 2039
 
2040
-		$share->setSharedWith('sharedWith')
2041
-			->setNode($path)
2042
-			->setShareOwner('shareOwner')
2043
-			->setProviderId('foo')
2044
-			->setId('bar');
2040
+        $share->setSharedWith('sharedWith')
2041
+            ->setNode($path)
2042
+            ->setShareOwner('shareOwner')
2043
+            ->setProviderId('foo')
2044
+            ->setId('bar');
2045 2045
 
2046
-		$share2 = $this->manager->newShare();
2047
-		$share2->setShareType(IShare::TYPE_GROUP)
2048
-			->setShareOwner('shareOwner2')
2049
-			->setProviderId('foo')
2050
-			->setId('baz')
2051
-			->setSharedWith('group');
2046
+        $share2 = $this->manager->newShare();
2047
+        $share2->setShareType(IShare::TYPE_GROUP)
2048
+            ->setShareOwner('shareOwner2')
2049
+            ->setProviderId('foo')
2050
+            ->setId('baz')
2051
+            ->setSharedWith('group');
2052 2052
 
2053
-		$this->groupManager->method('get')->with('group')->willReturn(null);
2053
+        $this->groupManager->method('get')->with('group')->willReturn(null);
2054 2054
 
2055
-		$this->defaultProvider
2056
-			->method('getSharesByPath')
2057
-			->with($path)
2058
-			->willReturn([$share2]);
2055
+        $this->defaultProvider
2056
+            ->method('getSharesByPath')
2057
+            ->with($path)
2058
+            ->willReturn([$share2]);
2059 2059
 
2060
-		$this->assertNull($this->invokePrivate($this->manager, 'userCreateChecks', [$share]));
2061
-	}
2060
+        $this->assertNull($this->invokePrivate($this->manager, 'userCreateChecks', [$share]));
2061
+    }
2062 2062
 
2063
-	public function testUserCreateChecksIdenticalPathNotSharedWithUser(): void {
2064
-		$share = $this->manager->newShare();
2065
-		$sharedWith = $this->createMock(IUser::class);
2066
-		$path = $this->createMock(Node::class);
2067
-		$share->setSharedWith('sharedWith')
2068
-			->setNode($path)
2069
-			->setShareOwner('shareOwner')
2070
-			->setProviderId('foo')
2071
-			->setId('bar');
2063
+    public function testUserCreateChecksIdenticalPathNotSharedWithUser(): void {
2064
+        $share = $this->manager->newShare();
2065
+        $sharedWith = $this->createMock(IUser::class);
2066
+        $path = $this->createMock(Node::class);
2067
+        $share->setSharedWith('sharedWith')
2068
+            ->setNode($path)
2069
+            ->setShareOwner('shareOwner')
2070
+            ->setProviderId('foo')
2071
+            ->setId('bar');
2072 2072
 
2073
-		$this->userManager->method('get')->with('sharedWith')->willReturn($sharedWith);
2073
+        $this->userManager->method('get')->with('sharedWith')->willReturn($sharedWith);
2074 2074
 
2075
-		$share2 = $this->manager->newShare();
2076
-		$share2->setShareType(IShare::TYPE_GROUP)
2077
-			->setShareOwner('shareOwner2')
2078
-			->setProviderId('foo')
2079
-			->setId('baz');
2075
+        $share2 = $this->manager->newShare();
2076
+        $share2->setShareType(IShare::TYPE_GROUP)
2077
+            ->setShareOwner('shareOwner2')
2078
+            ->setProviderId('foo')
2079
+            ->setId('baz');
2080 2080
 
2081
-		$group = $this->createMock(IGroup::class);
2082
-		$group->method('inGroup')
2083
-			->with($sharedWith)
2084
-			->willReturn(false);
2081
+        $group = $this->createMock(IGroup::class);
2082
+        $group->method('inGroup')
2083
+            ->with($sharedWith)
2084
+            ->willReturn(false);
2085 2085
 
2086
-		$this->groupManager->method('get')->with('group')->willReturn($group);
2086
+        $this->groupManager->method('get')->with('group')->willReturn($group);
2087 2087
 
2088
-		$share2->setSharedWith('group');
2088
+        $share2->setSharedWith('group');
2089 2089
 
2090
-		$this->defaultProvider
2091
-			->method('getSharesByPath')
2092
-			->with($path)
2093
-			->willReturn([$share2]);
2090
+        $this->defaultProvider
2091
+            ->method('getSharesByPath')
2092
+            ->with($path)
2093
+            ->willReturn([$share2]);
2094 2094
 
2095
-		self::invokePrivate($this->manager, 'userCreateChecks', [$share]);
2096
-		$this->addToAssertionCount(1);
2097
-	}
2095
+        self::invokePrivate($this->manager, 'userCreateChecks', [$share]);
2096
+        $this->addToAssertionCount(1);
2097
+    }
2098 2098
 
2099 2099
 
2100
-	public function testGroupCreateChecksShareWithGroupMembersGroupSharingNotAllowed(): void {
2101
-		$this->expectException(\Exception::class);
2102
-		$this->expectExceptionMessage('Group sharing is now allowed');
2100
+    public function testGroupCreateChecksShareWithGroupMembersGroupSharingNotAllowed(): void {
2101
+        $this->expectException(\Exception::class);
2102
+        $this->expectExceptionMessage('Group sharing is now allowed');
2103 2103
 
2104
-		$share = $this->manager->newShare();
2104
+        $share = $this->manager->newShare();
2105 2105
 
2106
-		$this->config
2107
-			->method('getAppValue')
2108
-			->willReturnMap([
2109
-				['core', 'shareapi_allow_group_sharing', 'yes', 'no'],
2110
-			]);
2106
+        $this->config
2107
+            ->method('getAppValue')
2108
+            ->willReturnMap([
2109
+                ['core', 'shareapi_allow_group_sharing', 'yes', 'no'],
2110
+            ]);
2111 2111
 
2112
-		self::invokePrivate($this->manager, 'groupCreateChecks', [$share]);
2113
-	}
2112
+        self::invokePrivate($this->manager, 'groupCreateChecks', [$share]);
2113
+    }
2114 2114
 
2115 2115
 
2116
-	public function testGroupCreateChecksShareWithGroupMembersOnlyNotInGroup(): void {
2117
-		$this->expectException(\Exception::class);
2118
-		$this->expectExceptionMessage('Sharing is only allowed within your own groups');
2116
+    public function testGroupCreateChecksShareWithGroupMembersOnlyNotInGroup(): void {
2117
+        $this->expectException(\Exception::class);
2118
+        $this->expectExceptionMessage('Sharing is only allowed within your own groups');
2119 2119
 
2120
-		$share = $this->manager->newShare();
2120
+        $share = $this->manager->newShare();
2121 2121
 
2122
-		$user = $this->createMock(IUser::class);
2123
-		$group = $this->createMock(IGroup::class);
2124
-		$share->setSharedBy('user')->setSharedWith('group');
2122
+        $user = $this->createMock(IUser::class);
2123
+        $group = $this->createMock(IGroup::class);
2124
+        $share->setSharedBy('user')->setSharedWith('group');
2125 2125
 
2126
-		$group->method('inGroup')->with($user)->willReturn(false);
2126
+        $group->method('inGroup')->with($user)->willReturn(false);
2127 2127
 
2128
-		$this->groupManager->method('get')->with('group')->willReturn($group);
2129
-		$this->userManager->method('get')->with('user')->willReturn($user);
2128
+        $this->groupManager->method('get')->with('group')->willReturn($group);
2129
+        $this->userManager->method('get')->with('user')->willReturn($user);
2130 2130
 
2131
-		$this->config
2132
-			->method('getAppValue')
2133
-			->willReturnMap([
2134
-				['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
2135
-				['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
2136
-				['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
2137
-			]);
2131
+        $this->config
2132
+            ->method('getAppValue')
2133
+            ->willReturnMap([
2134
+                ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
2135
+                ['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
2136
+                ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
2137
+            ]);
2138 2138
 
2139
-		self::invokePrivate($this->manager, 'groupCreateChecks', [$share]);
2140
-	}
2139
+        self::invokePrivate($this->manager, 'groupCreateChecks', [$share]);
2140
+    }
2141 2141
 
2142 2142
 
2143
-	public function testGroupCreateChecksShareWithGroupMembersOnlyNullGroup(): void {
2144
-		$this->expectException(\Exception::class);
2145
-		$this->expectExceptionMessage('Sharing is only allowed within your own groups');
2143
+    public function testGroupCreateChecksShareWithGroupMembersOnlyNullGroup(): void {
2144
+        $this->expectException(\Exception::class);
2145
+        $this->expectExceptionMessage('Sharing is only allowed within your own groups');
2146 2146
 
2147
-		$share = $this->manager->newShare();
2147
+        $share = $this->manager->newShare();
2148 2148
 
2149
-		$user = $this->createMock(IUser::class);
2150
-		$share->setSharedBy('user')->setSharedWith('group');
2149
+        $user = $this->createMock(IUser::class);
2150
+        $share->setSharedBy('user')->setSharedWith('group');
2151 2151
 
2152
-		$this->groupManager->method('get')->with('group')->willReturn(null);
2153
-		$this->userManager->method('get')->with('user')->willReturn($user);
2152
+        $this->groupManager->method('get')->with('group')->willReturn(null);
2153
+        $this->userManager->method('get')->with('user')->willReturn($user);
2154 2154
 
2155
-		$this->config
2156
-			->method('getAppValue')
2157
-			->willReturnMap([
2158
-				['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
2159
-				['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
2160
-				['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
2161
-			]);
2155
+        $this->config
2156
+            ->method('getAppValue')
2157
+            ->willReturnMap([
2158
+                ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
2159
+                ['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
2160
+                ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
2161
+            ]);
2162 2162
 
2163
-		$this->assertNull($this->invokePrivate($this->manager, 'groupCreateChecks', [$share]));
2164
-	}
2163
+        $this->assertNull($this->invokePrivate($this->manager, 'groupCreateChecks', [$share]));
2164
+    }
2165 2165
 
2166
-	public function testGroupCreateChecksShareWithGroupMembersOnlyInGroup(): void {
2167
-		$share = $this->manager->newShare();
2166
+    public function testGroupCreateChecksShareWithGroupMembersOnlyInGroup(): void {
2167
+        $share = $this->manager->newShare();
2168 2168
 
2169
-		$user = $this->createMock(IUser::class);
2170
-		$group = $this->createMock(IGroup::class);
2171
-		$share->setSharedBy('user')->setSharedWith('group');
2169
+        $user = $this->createMock(IUser::class);
2170
+        $group = $this->createMock(IGroup::class);
2171
+        $share->setSharedBy('user')->setSharedWith('group');
2172 2172
 
2173
-		$this->userManager->method('get')->with('user')->willReturn($user);
2174
-		$this->groupManager->method('get')->with('group')->willReturn($group);
2173
+        $this->userManager->method('get')->with('user')->willReturn($user);
2174
+        $this->groupManager->method('get')->with('group')->willReturn($group);
2175 2175
 
2176
-		$group->method('inGroup')->with($user)->willReturn(true);
2176
+        $group->method('inGroup')->with($user)->willReturn(true);
2177 2177
 
2178
-		$path = $this->createMock(Node::class);
2179
-		$share->setNode($path);
2178
+        $path = $this->createMock(Node::class);
2179
+        $share->setNode($path);
2180 2180
 
2181
-		$this->defaultProvider->method('getSharesByPath')
2182
-			->with($path)
2183
-			->willReturn([]);
2181
+        $this->defaultProvider->method('getSharesByPath')
2182
+            ->with($path)
2183
+            ->willReturn([]);
2184 2184
 
2185
-		$this->config
2186
-			->method('getAppValue')
2187
-			->willReturnMap([
2188
-				['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
2189
-				['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
2190
-				['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
2191
-			]);
2185
+        $this->config
2186
+            ->method('getAppValue')
2187
+            ->willReturnMap([
2188
+                ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
2189
+                ['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
2190
+                ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
2191
+            ]);
2192 2192
 
2193
-		self::invokePrivate($this->manager, 'groupCreateChecks', [$share]);
2194
-		$this->addToAssertionCount(1);
2195
-	}
2193
+        self::invokePrivate($this->manager, 'groupCreateChecks', [$share]);
2194
+        $this->addToAssertionCount(1);
2195
+    }
2196 2196
 
2197 2197
 
2198
-	public function testGroupCreateChecksPathAlreadySharedWithSameGroup(): void {
2199
-		$this->expectException(\Exception::class);
2200
-		$this->expectExceptionMessage('Path is already shared with this group');
2198
+    public function testGroupCreateChecksPathAlreadySharedWithSameGroup(): void {
2199
+        $this->expectException(\Exception::class);
2200
+        $this->expectExceptionMessage('Path is already shared with this group');
2201 2201
 
2202
-		$share = $this->manager->newShare();
2202
+        $share = $this->manager->newShare();
2203 2203
 
2204
-		$path = $this->createMock(Node::class);
2205
-		$share->setSharedWith('sharedWith')
2206
-			->setNode($path)
2207
-			->setProviderId('foo')
2208
-			->setId('bar');
2204
+        $path = $this->createMock(Node::class);
2205
+        $share->setSharedWith('sharedWith')
2206
+            ->setNode($path)
2207
+            ->setProviderId('foo')
2208
+            ->setId('bar');
2209 2209
 
2210
-		$share2 = $this->manager->newShare();
2211
-		$share2->setSharedWith('sharedWith')
2212
-			->setProviderId('foo')
2213
-			->setId('baz');
2210
+        $share2 = $this->manager->newShare();
2211
+        $share2->setSharedWith('sharedWith')
2212
+            ->setProviderId('foo')
2213
+            ->setId('baz');
2214 2214
 
2215
-		$this->defaultProvider->method('getSharesByPath')
2216
-			->with($path)
2217
-			->willReturn([$share2]);
2215
+        $this->defaultProvider->method('getSharesByPath')
2216
+            ->with($path)
2217
+            ->willReturn([$share2]);
2218 2218
 
2219
-		$this->config
2220
-			->method('getAppValue')
2221
-			->willReturnMap([
2222
-				['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
2223
-			]);
2219
+        $this->config
2220
+            ->method('getAppValue')
2221
+            ->willReturnMap([
2222
+                ['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
2223
+            ]);
2224 2224
 
2225
-		self::invokePrivate($this->manager, 'groupCreateChecks', [$share]);
2226
-	}
2225
+        self::invokePrivate($this->manager, 'groupCreateChecks', [$share]);
2226
+    }
2227 2227
 
2228
-	public function testGroupCreateChecksPathAlreadySharedWithDifferentGroup(): void {
2229
-		$share = $this->manager->newShare();
2228
+    public function testGroupCreateChecksPathAlreadySharedWithDifferentGroup(): void {
2229
+        $share = $this->manager->newShare();
2230 2230
 
2231
-		$share->setSharedWith('sharedWith');
2231
+        $share->setSharedWith('sharedWith');
2232 2232
 
2233
-		$path = $this->createMock(Node::class);
2234
-		$share->setNode($path);
2233
+        $path = $this->createMock(Node::class);
2234
+        $share->setNode($path);
2235 2235
 
2236
-		$share2 = $this->manager->newShare();
2237
-		$share2->setSharedWith('sharedWith2');
2236
+        $share2 = $this->manager->newShare();
2237
+        $share2->setSharedWith('sharedWith2');
2238 2238
 
2239
-		$this->defaultProvider->method('getSharesByPath')
2240
-			->with($path)
2241
-			->willReturn([$share2]);
2239
+        $this->defaultProvider->method('getSharesByPath')
2240
+            ->with($path)
2241
+            ->willReturn([$share2]);
2242 2242
 
2243
-		$this->config
2244
-			->method('getAppValue')
2245
-			->willReturnMap([
2246
-				['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
2247
-			]);
2243
+        $this->config
2244
+            ->method('getAppValue')
2245
+            ->willReturnMap([
2246
+                ['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
2247
+            ]);
2248 2248
 
2249
-		self::invokePrivate($this->manager, 'groupCreateChecks', [$share]);
2250
-		$this->addToAssertionCount(1);
2251
-	}
2249
+        self::invokePrivate($this->manager, 'groupCreateChecks', [$share]);
2250
+        $this->addToAssertionCount(1);
2251
+    }
2252 2252
 
2253 2253
 
2254
-	public function testLinkCreateChecksNoLinkSharesAllowed(): void {
2255
-		$this->expectException(\Exception::class);
2256
-		$this->expectExceptionMessage('Link sharing is not allowed');
2254
+    public function testLinkCreateChecksNoLinkSharesAllowed(): void {
2255
+        $this->expectException(\Exception::class);
2256
+        $this->expectExceptionMessage('Link sharing is not allowed');
2257 2257
 
2258
-		$share = $this->manager->newShare();
2258
+        $share = $this->manager->newShare();
2259 2259
 
2260
-		$this->config
2261
-			->method('getAppValue')
2262
-			->willReturnMap([
2263
-				['core', 'shareapi_allow_links', 'yes', 'no'],
2264
-			]);
2260
+        $this->config
2261
+            ->method('getAppValue')
2262
+            ->willReturnMap([
2263
+                ['core', 'shareapi_allow_links', 'yes', 'no'],
2264
+            ]);
2265 2265
 
2266
-		self::invokePrivate($this->manager, 'linkCreateChecks', [$share]);
2267
-	}
2266
+        self::invokePrivate($this->manager, 'linkCreateChecks', [$share]);
2267
+    }
2268 2268
 
2269 2269
 
2270
-	public function testFileLinkCreateChecksNoPublicUpload(): void {
2271
-		$share = $this->manager->newShare();
2270
+    public function testFileLinkCreateChecksNoPublicUpload(): void {
2271
+        $share = $this->manager->newShare();
2272 2272
 
2273
-		$share->setPermissions(Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE);
2274
-		$share->setNodeType('file');
2273
+        $share->setPermissions(Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE);
2274
+        $share->setNodeType('file');
2275 2275
 
2276
-		$this->config
2277
-			->method('getAppValue')
2278
-			->willReturnMap([
2279
-				['core', 'shareapi_allow_links', 'yes', 'yes'],
2280
-				['core', 'shareapi_allow_public_upload', 'yes', 'no']
2281
-			]);
2276
+        $this->config
2277
+            ->method('getAppValue')
2278
+            ->willReturnMap([
2279
+                ['core', 'shareapi_allow_links', 'yes', 'yes'],
2280
+                ['core', 'shareapi_allow_public_upload', 'yes', 'no']
2281
+            ]);
2282 2282
 
2283
-		self::invokePrivate($this->manager, 'linkCreateChecks', [$share]);
2284
-		$this->addToAssertionCount(1);
2285
-	}
2283
+        self::invokePrivate($this->manager, 'linkCreateChecks', [$share]);
2284
+        $this->addToAssertionCount(1);
2285
+    }
2286 2286
 
2287
-	public function testFolderLinkCreateChecksNoPublicUpload(): void {
2288
-		$this->expectException(\Exception::class);
2289
-		$this->expectExceptionMessage('Public upload is not allowed');
2287
+    public function testFolderLinkCreateChecksNoPublicUpload(): void {
2288
+        $this->expectException(\Exception::class);
2289
+        $this->expectExceptionMessage('Public upload is not allowed');
2290 2290
 
2291
-		$share = $this->manager->newShare();
2291
+        $share = $this->manager->newShare();
2292 2292
 
2293
-		$share->setPermissions(Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE);
2294
-		$share->setNodeType('folder');
2293
+        $share->setPermissions(Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE);
2294
+        $share->setNodeType('folder');
2295 2295
 
2296
-		$this->config
2297
-			->method('getAppValue')
2298
-			->willReturnMap([
2299
-				['core', 'shareapi_allow_links', 'yes', 'yes'],
2300
-				['core', 'shareapi_allow_public_upload', 'yes', 'no']
2301
-			]);
2296
+        $this->config
2297
+            ->method('getAppValue')
2298
+            ->willReturnMap([
2299
+                ['core', 'shareapi_allow_links', 'yes', 'yes'],
2300
+                ['core', 'shareapi_allow_public_upload', 'yes', 'no']
2301
+            ]);
2302 2302
 
2303
-		self::invokePrivate($this->manager, 'linkCreateChecks', [$share]);
2304
-	}
2303
+        self::invokePrivate($this->manager, 'linkCreateChecks', [$share]);
2304
+    }
2305 2305
 
2306
-	public function testLinkCreateChecksPublicUpload(): void {
2307
-		$share = $this->manager->newShare();
2306
+    public function testLinkCreateChecksPublicUpload(): void {
2307
+        $share = $this->manager->newShare();
2308 2308
 
2309
-		$share->setPermissions(Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE);
2310
-		$share->setSharedWith('sharedWith');
2311
-		$folder = $this->createMock(\OC\Files\Node\Folder::class);
2312
-		$share->setNode($folder);
2309
+        $share->setPermissions(Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE);
2310
+        $share->setSharedWith('sharedWith');
2311
+        $folder = $this->createMock(\OC\Files\Node\Folder::class);
2312
+        $share->setNode($folder);
2313 2313
 
2314
-		$this->config
2315
-			->method('getAppValue')
2316
-			->willReturnMap([
2317
-				['core', 'shareapi_allow_links', 'yes', 'yes'],
2318
-				['core', 'shareapi_allow_public_upload', 'yes', 'yes']
2319
-			]);
2314
+        $this->config
2315
+            ->method('getAppValue')
2316
+            ->willReturnMap([
2317
+                ['core', 'shareapi_allow_links', 'yes', 'yes'],
2318
+                ['core', 'shareapi_allow_public_upload', 'yes', 'yes']
2319
+            ]);
2320 2320
 
2321
-		self::invokePrivate($this->manager, 'linkCreateChecks', [$share]);
2322
-		$this->addToAssertionCount(1);
2323
-	}
2321
+        self::invokePrivate($this->manager, 'linkCreateChecks', [$share]);
2322
+        $this->addToAssertionCount(1);
2323
+    }
2324 2324
 
2325
-	public function testLinkCreateChecksReadOnly(): void {
2326
-		$share = $this->manager->newShare();
2325
+    public function testLinkCreateChecksReadOnly(): void {
2326
+        $share = $this->manager->newShare();
2327 2327
 
2328
-		$share->setPermissions(Constants::PERMISSION_READ);
2329
-		$share->setSharedWith('sharedWith');
2330
-		$folder = $this->createMock(\OC\Files\Node\Folder::class);
2331
-		$share->setNode($folder);
2328
+        $share->setPermissions(Constants::PERMISSION_READ);
2329
+        $share->setSharedWith('sharedWith');
2330
+        $folder = $this->createMock(\OC\Files\Node\Folder::class);
2331
+        $share->setNode($folder);
2332 2332
 
2333
-		$this->config
2334
-			->method('getAppValue')
2335
-			->willReturnMap([
2336
-				['core', 'shareapi_allow_links', 'yes', 'yes'],
2337
-				['core', 'shareapi_allow_public_upload', 'yes', 'no']
2338
-			]);
2333
+        $this->config
2334
+            ->method('getAppValue')
2335
+            ->willReturnMap([
2336
+                ['core', 'shareapi_allow_links', 'yes', 'yes'],
2337
+                ['core', 'shareapi_allow_public_upload', 'yes', 'no']
2338
+            ]);
2339 2339
 
2340
-		self::invokePrivate($this->manager, 'linkCreateChecks', [$share]);
2341
-		$this->addToAssertionCount(1);
2342
-	}
2343
-
2344
-
2345
-	public function testPathCreateChecksContainsSharedMount(): void {
2346
-		$this->expectException(\InvalidArgumentException::class);
2347
-		$this->expectExceptionMessage('You cannot share a folder that contains other shares');
2340
+        self::invokePrivate($this->manager, 'linkCreateChecks', [$share]);
2341
+        $this->addToAssertionCount(1);
2342
+    }
2343
+
2344
+
2345
+    public function testPathCreateChecksContainsSharedMount(): void {
2346
+        $this->expectException(\InvalidArgumentException::class);
2347
+        $this->expectExceptionMessage('You cannot share a folder that contains other shares');
2348 2348
 
2349
-		$path = $this->createMock(Folder::class);
2350
-		$path->method('getPath')->willReturn('path');
2349
+        $path = $this->createMock(Folder::class);
2350
+        $path->method('getPath')->willReturn('path');
2351 2351
 
2352
-		$mount = $this->createMock(IMountPoint::class);
2353
-		$storage = $this->createMock(IStorage::class);
2354
-		$mount->method('getStorage')->willReturn($storage);
2355
-		$storage->method('instanceOfStorage')->with('\OCA\Files_Sharing\ISharedStorage')->willReturn(true);
2356
-
2357
-		$this->mountManager->method('findIn')->with('path')->willReturn([$mount]);
2358
-
2359
-		self::invokePrivate($this->manager, 'pathCreateChecks', [$path]);
2360
-	}
2361
-
2362
-	public function testPathCreateChecksContainsNoSharedMount(): void {
2363
-		$path = $this->createMock(Folder::class);
2364
-		$path->method('getPath')->willReturn('path');
2352
+        $mount = $this->createMock(IMountPoint::class);
2353
+        $storage = $this->createMock(IStorage::class);
2354
+        $mount->method('getStorage')->willReturn($storage);
2355
+        $storage->method('instanceOfStorage')->with('\OCA\Files_Sharing\ISharedStorage')->willReturn(true);
2356
+
2357
+        $this->mountManager->method('findIn')->with('path')->willReturn([$mount]);
2358
+
2359
+        self::invokePrivate($this->manager, 'pathCreateChecks', [$path]);
2360
+    }
2361
+
2362
+    public function testPathCreateChecksContainsNoSharedMount(): void {
2363
+        $path = $this->createMock(Folder::class);
2364
+        $path->method('getPath')->willReturn('path');
2365 2365
 
2366
-		$mount = $this->createMock(IMountPoint::class);
2367
-		$storage = $this->createMock(IStorage::class);
2368
-		$mount->method('getStorage')->willReturn($storage);
2369
-		$storage->method('instanceOfStorage')->with('\OCA\Files_Sharing\ISharedStorage')->willReturn(false);
2370
-
2371
-		$this->mountManager->method('findIn')->with('path')->willReturn([$mount]);
2372
-
2373
-		self::invokePrivate($this->manager, 'pathCreateChecks', [$path]);
2374
-		$this->addToAssertionCount(1);
2375
-	}
2376
-
2377
-	public function testPathCreateChecksContainsNoFolder(): void {
2378
-		$path = $this->createMock(File::class);
2379
-
2380
-		self::invokePrivate($this->manager, 'pathCreateChecks', [$path]);
2381
-		$this->addToAssertionCount(1);
2382
-	}
2383
-
2384
-	public static function dataIsSharingDisabledForUser() {
2385
-		$data = [];
2386
-
2387
-		// No exclude groups
2388
-		$data[] = ['no', null, null, [], false];
2389
-
2390
-		// empty exclude / allow list, user no groups
2391
-		$data[] = ['yes', '', json_encode(['']), [], false];
2392
-		$data[] = ['allow', '', json_encode(['']), [], true];
2393
-
2394
-		// empty exclude / allow list, user groups
2395
-		$data[] = ['yes', '', json_encode(['']), ['group1', 'group2'], false];
2396
-		$data[] = ['allow', '', json_encode(['']), ['group1', 'group2'], true];
2397
-
2398
-		// Convert old list to json
2399
-		$data[] = ['yes', 'group1,group2', json_encode(['group1', 'group2']), [], false];
2400
-		$data[] = ['allow', 'group1,group2', json_encode(['group1', 'group2']), [], true];
2401
-
2402
-		// Old list partly groups in common
2403
-		$data[] = ['yes', 'group1,group2', json_encode(['group1', 'group2']), ['group1', 'group3'], false];
2404
-		$data[] = ['allow', 'group1,group2', json_encode(['group1', 'group2']), ['group1', 'group3'], false];
2405
-
2406
-		// Old list only groups in common
2407
-		$data[] = ['yes', 'group1,group2', json_encode(['group1', 'group2']), ['group1'], true];
2408
-		$data[] = ['allow', 'group1,group2', json_encode(['group1', 'group2']), ['group1'], false];
2409
-
2410
-		// New list partly in common
2411
-		$data[] = ['yes', json_encode(['group1', 'group2']), null, ['group1', 'group3'], false];
2412
-		$data[] = ['allow', json_encode(['group1', 'group2']), null, ['group1', 'group3'], false];
2413
-
2414
-		// New list only groups in common
2415
-		$data[] = ['yes', json_encode(['group1', 'group2']), null, ['group2'], true];
2416
-		$data[] = ['allow', json_encode(['group1', 'group2']), null, ['group2'], false];
2417
-
2418
-		return $data;
2419
-	}
2366
+        $mount = $this->createMock(IMountPoint::class);
2367
+        $storage = $this->createMock(IStorage::class);
2368
+        $mount->method('getStorage')->willReturn($storage);
2369
+        $storage->method('instanceOfStorage')->with('\OCA\Files_Sharing\ISharedStorage')->willReturn(false);
2370
+
2371
+        $this->mountManager->method('findIn')->with('path')->willReturn([$mount]);
2372
+
2373
+        self::invokePrivate($this->manager, 'pathCreateChecks', [$path]);
2374
+        $this->addToAssertionCount(1);
2375
+    }
2376
+
2377
+    public function testPathCreateChecksContainsNoFolder(): void {
2378
+        $path = $this->createMock(File::class);
2379
+
2380
+        self::invokePrivate($this->manager, 'pathCreateChecks', [$path]);
2381
+        $this->addToAssertionCount(1);
2382
+    }
2383
+
2384
+    public static function dataIsSharingDisabledForUser() {
2385
+        $data = [];
2386
+
2387
+        // No exclude groups
2388
+        $data[] = ['no', null, null, [], false];
2389
+
2390
+        // empty exclude / allow list, user no groups
2391
+        $data[] = ['yes', '', json_encode(['']), [], false];
2392
+        $data[] = ['allow', '', json_encode(['']), [], true];
2393
+
2394
+        // empty exclude / allow list, user groups
2395
+        $data[] = ['yes', '', json_encode(['']), ['group1', 'group2'], false];
2396
+        $data[] = ['allow', '', json_encode(['']), ['group1', 'group2'], true];
2397
+
2398
+        // Convert old list to json
2399
+        $data[] = ['yes', 'group1,group2', json_encode(['group1', 'group2']), [], false];
2400
+        $data[] = ['allow', 'group1,group2', json_encode(['group1', 'group2']), [], true];
2401
+
2402
+        // Old list partly groups in common
2403
+        $data[] = ['yes', 'group1,group2', json_encode(['group1', 'group2']), ['group1', 'group3'], false];
2404
+        $data[] = ['allow', 'group1,group2', json_encode(['group1', 'group2']), ['group1', 'group3'], false];
2405
+
2406
+        // Old list only groups in common
2407
+        $data[] = ['yes', 'group1,group2', json_encode(['group1', 'group2']), ['group1'], true];
2408
+        $data[] = ['allow', 'group1,group2', json_encode(['group1', 'group2']), ['group1'], false];
2409
+
2410
+        // New list partly in common
2411
+        $data[] = ['yes', json_encode(['group1', 'group2']), null, ['group1', 'group3'], false];
2412
+        $data[] = ['allow', json_encode(['group1', 'group2']), null, ['group1', 'group3'], false];
2413
+
2414
+        // New list only groups in common
2415
+        $data[] = ['yes', json_encode(['group1', 'group2']), null, ['group2'], true];
2416
+        $data[] = ['allow', json_encode(['group1', 'group2']), null, ['group2'], false];
2417
+
2418
+        return $data;
2419
+    }
2420 2420
 
2421
-	/**
2422
-	 *
2423
-	 * @param string $excludeGroups
2424
-	 * @param string $groupList
2425
-	 * @param string $setList
2426
-	 * @param string[] $groupIds
2427
-	 * @param bool $expected
2428
-	 */
2429
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataIsSharingDisabledForUser')]
2430
-	public function testIsSharingDisabledForUser($excludeGroups, $groupList, $setList, $groupIds, $expected): void {
2431
-		$user = $this->createMock(IUser::class);
2421
+    /**
2422
+     *
2423
+     * @param string $excludeGroups
2424
+     * @param string $groupList
2425
+     * @param string $setList
2426
+     * @param string[] $groupIds
2427
+     * @param bool $expected
2428
+     */
2429
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataIsSharingDisabledForUser')]
2430
+    public function testIsSharingDisabledForUser($excludeGroups, $groupList, $setList, $groupIds, $expected): void {
2431
+        $user = $this->createMock(IUser::class);
2432 2432
 
2433
-		$this->config->method('getAppValue')
2434
-			->willReturnMap([
2435
-				['core', 'shareapi_exclude_groups', 'no', $excludeGroups],
2436
-				['core', 'shareapi_exclude_groups_list', '', $groupList],
2437
-			]);
2433
+        $this->config->method('getAppValue')
2434
+            ->willReturnMap([
2435
+                ['core', 'shareapi_exclude_groups', 'no', $excludeGroups],
2436
+                ['core', 'shareapi_exclude_groups_list', '', $groupList],
2437
+            ]);
2438 2438
 
2439
-		if ($setList !== null) {
2440
-			$this->config->expects($this->once())
2441
-				->method('setAppValue')
2442
-				->with('core', 'shareapi_exclude_groups_list', $setList);
2443
-		} else {
2444
-			$this->config->expects($this->never())
2445
-				->method('setAppValue');
2446
-		}
2439
+        if ($setList !== null) {
2440
+            $this->config->expects($this->once())
2441
+                ->method('setAppValue')
2442
+                ->with('core', 'shareapi_exclude_groups_list', $setList);
2443
+        } else {
2444
+            $this->config->expects($this->never())
2445
+                ->method('setAppValue');
2446
+        }
2447 2447
 
2448
-		$this->groupManager->method('getUserGroupIds')
2449
-			->with($user)
2450
-			->willReturn($groupIds);
2448
+        $this->groupManager->method('getUserGroupIds')
2449
+            ->with($user)
2450
+            ->willReturn($groupIds);
2451 2451
 
2452
-		$this->userManager->method('get')->with('user')->willReturn($user);
2453
-
2454
-		$res = $this->manager->sharingDisabledForUser('user');
2455
-		$this->assertEquals($expected, $res);
2456
-	}
2457
-
2458
-	public static function dataCanShare() {
2459
-		$data = [];
2460
-
2461
-		/*
2452
+        $this->userManager->method('get')->with('user')->willReturn($user);
2453
+
2454
+        $res = $this->manager->sharingDisabledForUser('user');
2455
+        $this->assertEquals($expected, $res);
2456
+    }
2457
+
2458
+    public static function dataCanShare() {
2459
+        $data = [];
2460
+
2461
+        /*
2462 2462
 		 * [expected, sharing enabled, disabled for user]
2463 2463
 		 */
2464 2464
 
2465
-		$data[] = [false, 'no', false];
2466
-		$data[] = [false, 'no', true];
2467
-		$data[] = [true, 'yes', false];
2468
-		$data[] = [false, 'yes', true];
2469
-
2470
-		return $data;
2471
-	}
2472
-
2473
-	/**
2474
-	 *
2475
-	 * @param bool $expected
2476
-	 * @param string $sharingEnabled
2477
-	 * @param bool $disabledForUser
2478
-	 */
2479
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataCanShare')]
2480
-	public function testCanShare($expected, $sharingEnabled, $disabledForUser): void {
2481
-		$this->config->method('getAppValue')
2482
-			->willReturnMap([
2483
-				['core', 'shareapi_enabled', 'yes', $sharingEnabled],
2484
-			]);
2485
-
2486
-		$manager = $this->createManagerMock()
2487
-			->onlyMethods(['sharingDisabledForUser'])
2488
-			->getMock();
2489
-
2490
-		$manager->method('sharingDisabledForUser')
2491
-			->with('user')
2492
-			->willReturn($disabledForUser);
2493
-
2494
-		$share = $this->manager->newShare();
2495
-		$share->setSharedBy('user');
2496
-
2497
-		$exception = false;
2498
-		try {
2499
-			$res = self::invokePrivate($manager, 'canShare', [$share]);
2500
-		} catch (\Exception $e) {
2501
-			$exception = true;
2502
-		}
2503
-
2504
-		$this->assertEquals($expected, !$exception);
2505
-	}
2506
-
2507
-	public function testCreateShareUser(): void {
2508
-		/** @var Manager&MockObject $manager */
2509
-		$manager = $this->createManagerMock()
2510
-			->onlyMethods(['canShare', 'generalCreateChecks', 'userCreateChecks', 'pathCreateChecks'])
2511
-			->getMock();
2512
-
2513
-		$shareOwner = $this->createMock(IUser::class);
2514
-		$shareOwner->method('getUID')->willReturn('shareOwner');
2515
-
2516
-		$storage = $this->createMock(IStorage::class);
2517
-		$path = $this->createMock(File::class);
2518
-		$path->method('getOwner')->willReturn($shareOwner);
2519
-		$path->method('getName')->willReturn('target');
2520
-		$path->method('getStorage')->willReturn($storage);
2521
-
2522
-		$share = $this->createShare(
2523
-			null,
2524
-			IShare::TYPE_USER,
2525
-			$path,
2526
-			'sharedWith',
2527
-			'sharedBy',
2528
-			null,
2529
-			Constants::PERMISSION_ALL);
2530
-
2531
-		$manager->expects($this->once())
2532
-			->method('canShare')
2533
-			->with($share)
2534
-			->willReturn(true);
2535
-		$manager->expects($this->once())
2536
-			->method('generalCreateChecks')
2537
-			->with($share);
2538
-		;
2539
-		$manager->expects($this->once())
2540
-			->method('userCreateChecks')
2541
-			->with($share);
2542
-		;
2543
-		$manager->expects($this->once())
2544
-			->method('pathCreateChecks')
2545
-			->with($path);
2546
-
2547
-		$this->defaultProvider
2548
-			->expects($this->once())
2549
-			->method('create')
2550
-			->with($share)
2551
-			->willReturnArgument(0);
2552
-
2553
-		$share->expects($this->once())
2554
-			->method('setShareOwner')
2555
-			->with('shareOwner');
2556
-		$share->expects($this->once())
2557
-			->method('setTarget')
2558
-			->with('/target');
2559
-
2560
-		$manager->createShare($share);
2561
-	}
2562
-
2563
-	public function testCreateShareGroup(): void {
2564
-		$manager = $this->createManagerMock()
2565
-			->onlyMethods(['canShare', 'generalCreateChecks', 'groupCreateChecks', 'pathCreateChecks'])
2566
-			->getMock();
2567
-
2568
-		$shareOwner = $this->createMock(IUser::class);
2569
-		$shareOwner->method('getUID')->willReturn('shareOwner');
2570
-
2571
-		$storage = $this->createMock(IStorage::class);
2572
-		$path = $this->createMock(File::class);
2573
-		$path->method('getOwner')->willReturn($shareOwner);
2574
-		$path->method('getName')->willReturn('target');
2575
-		$path->method('getStorage')->willReturn($storage);
2576
-
2577
-		$share = $this->createShare(
2578
-			null,
2579
-			IShare::TYPE_GROUP,
2580
-			$path,
2581
-			'sharedWith',
2582
-			'sharedBy',
2583
-			null,
2584
-			Constants::PERMISSION_ALL);
2585
-
2586
-		$manager->expects($this->once())
2587
-			->method('canShare')
2588
-			->with($share)
2589
-			->willReturn(true);
2590
-		$manager->expects($this->once())
2591
-			->method('generalCreateChecks')
2592
-			->with($share);
2593
-		;
2594
-		$manager->expects($this->once())
2595
-			->method('groupCreateChecks')
2596
-			->with($share);
2597
-		;
2598
-		$manager->expects($this->once())
2599
-			->method('pathCreateChecks')
2600
-			->with($path);
2601
-
2602
-		$this->defaultProvider
2603
-			->expects($this->once())
2604
-			->method('create')
2605
-			->with($share)
2606
-			->willReturnArgument(0);
2607
-
2608
-		$share->expects($this->once())
2609
-			->method('setShareOwner')
2610
-			->with('shareOwner');
2611
-		$share->expects($this->once())
2612
-			->method('setTarget')
2613
-			->with('/target');
2614
-
2615
-		$manager->createShare($share);
2616
-	}
2617
-
2618
-	public function testCreateShareLink(): void {
2619
-		$manager = $this->createManagerMock()
2620
-			->onlyMethods([
2621
-				'canShare',
2622
-				'generalCreateChecks',
2623
-				'linkCreateChecks',
2624
-				'pathCreateChecks',
2625
-				'validateExpirationDateLink',
2626
-				'verifyPassword',
2627
-				'setLinkParent',
2628
-			])
2629
-			->getMock();
2630
-
2631
-		$shareOwner = $this->createMock(IUser::class);
2632
-		$shareOwner->method('getUID')->willReturn('shareOwner');
2633
-
2634
-		$storage = $this->createMock(IStorage::class);
2635
-		$path = $this->createMock(File::class);
2636
-		$path->method('getOwner')->willReturn($shareOwner);
2637
-		$path->method('getName')->willReturn('target');
2638
-		$path->method('getId')->willReturn(1);
2639
-		$path->method('getStorage')->willReturn($storage);
2640
-
2641
-		$date = new \DateTime();
2642
-
2643
-		$share = $this->manager->newShare();
2644
-		$share->setShareType(IShare::TYPE_LINK)
2645
-			->setNode($path)
2646
-			->setSharedBy('sharedBy')
2647
-			->setPermissions(Constants::PERMISSION_ALL)
2648
-			->setExpirationDate($date)
2649
-			->setPassword('password');
2650
-
2651
-		$manager->expects($this->once())
2652
-			->method('canShare')
2653
-			->with($share)
2654
-			->willReturn(true);
2655
-		$manager->expects($this->once())
2656
-			->method('generalCreateChecks')
2657
-			->with($share);
2658
-		;
2659
-		$manager->expects($this->once())
2660
-			->method('linkCreateChecks')
2661
-			->with($share);
2662
-		;
2663
-		$manager->expects($this->once())
2664
-			->method('pathCreateChecks')
2665
-			->with($path);
2666
-		$manager->expects($this->once())
2667
-			->method('validateExpirationDateLink')
2668
-			->with($share)
2669
-			->willReturn($share);
2670
-		$manager->expects($this->once())
2671
-			->method('verifyPassword')
2672
-			->with('password');
2673
-		$manager->expects($this->once())
2674
-			->method('setLinkParent')
2675
-			->with($share);
2676
-
2677
-		$this->hasher->expects($this->once())
2678
-			->method('hash')
2679
-			->with('password')
2680
-			->willReturn('hashed');
2681
-
2682
-		$this->secureRandom->method('generate')
2683
-			->willReturn('token');
2684
-
2685
-		$this->defaultProvider
2686
-			->expects($this->once())
2687
-			->method('create')
2688
-			->with($share)
2689
-			->willReturnCallback(function (Share $share) {
2690
-				return $share->setId(42);
2691
-			});
2692
-
2693
-		$calls = [
2694
-			BeforeShareCreatedEvent::class,
2695
-			ShareCreatedEvent::class,
2696
-		];
2697
-		$this->dispatcher->expects($this->exactly(2))
2698
-			->method('dispatchTyped')
2699
-			->willReturnCallback(function ($event) use (&$calls, $date, $path): void {
2700
-				$expected = array_shift($calls);
2701
-				$this->assertInstanceOf($expected, $event);
2702
-				$share = $event->getShare();
2703
-
2704
-				$this->assertEquals(IShare::TYPE_LINK, $share->getShareType(), 'getShareType');
2705
-				$this->assertEquals($path, $share->getNode(), 'getNode');
2706
-				$this->assertEquals('sharedBy', $share->getSharedBy(), 'getSharedBy');
2707
-				$this->assertEquals(Constants::PERMISSION_ALL, $share->getPermissions(), 'getPermissions');
2708
-				$this->assertEquals($date, $share->getExpirationDate(), 'getExpirationDate');
2709
-				$this->assertEquals('hashed', $share->getPassword(), 'getPassword');
2710
-				$this->assertEquals('token', $share->getToken(), 'getToken');
2711
-
2712
-				if ($expected === ShareCreatedEvent::class) {
2713
-					$this->assertEquals('42', $share->getId(), 'getId');
2714
-					$this->assertEquals('/target', $share->getTarget(), 'getTarget');
2715
-				}
2716
-			});
2717
-
2718
-		/** @var IShare $share */
2719
-		$share = $manager->createShare($share);
2720
-
2721
-		$this->assertSame('shareOwner', $share->getShareOwner());
2722
-		$this->assertEquals('/target', $share->getTarget());
2723
-		$this->assertSame($date, $share->getExpirationDate());
2724
-		$this->assertEquals('token', $share->getToken());
2725
-		$this->assertEquals('hashed', $share->getPassword());
2726
-	}
2727
-
2728
-	public function testCreateShareMail(): void {
2729
-		$manager = $this->createManagerMock()
2730
-			->onlyMethods([
2731
-				'canShare',
2732
-				'generalCreateChecks',
2733
-				'linkCreateChecks',
2734
-				'pathCreateChecks',
2735
-				'validateExpirationDateLink',
2736
-				'verifyPassword',
2737
-				'setLinkParent',
2738
-			])
2739
-			->getMock();
2740
-
2741
-		$shareOwner = $this->createMock(IUser::class);
2742
-		$shareOwner->method('getUID')->willReturn('shareOwner');
2743
-
2744
-		$storage = $this->createMock(IStorage::class);
2745
-		$path = $this->createMock(File::class);
2746
-		$path->method('getOwner')->willReturn($shareOwner);
2747
-		$path->method('getName')->willReturn('target');
2748
-		$path->method('getId')->willReturn(1);
2749
-		$path->method('getStorage')->willReturn($storage);
2750
-
2751
-		$share = $this->manager->newShare();
2752
-		$share->setShareType(IShare::TYPE_EMAIL)
2753
-			->setNode($path)
2754
-			->setSharedBy('sharedBy')
2755
-			->setPermissions(Constants::PERMISSION_ALL);
2756
-
2757
-		$manager->expects($this->once())
2758
-			->method('canShare')
2759
-			->with($share)
2760
-			->willReturn(true);
2761
-		$manager->expects($this->once())
2762
-			->method('generalCreateChecks')
2763
-			->with($share);
2764
-
2765
-		$manager->expects($this->once())
2766
-			->method('linkCreateChecks');
2767
-		$manager->expects($this->once())
2768
-			->method('pathCreateChecks')
2769
-			->with($path);
2770
-		$manager->expects($this->once())
2771
-			->method('validateExpirationDateLink')
2772
-			->with($share)
2773
-			->willReturn($share);
2774
-		$manager->expects($this->once())
2775
-			->method('verifyPassword');
2776
-		$manager->expects($this->once())
2777
-			->method('setLinkParent');
2778
-
2779
-		$this->secureRandom->method('generate')
2780
-			->willReturn('token');
2781
-
2782
-		$this->defaultProvider
2783
-			->expects($this->once())
2784
-			->method('create')
2785
-			->with($share)
2786
-			->willReturnCallback(function (Share $share) {
2787
-				return $share->setId(42);
2788
-			});
2789
-
2790
-		$calls = [
2791
-			BeforeShareCreatedEvent::class,
2792
-			ShareCreatedEvent::class,
2793
-		];
2794
-		$this->dispatcher->expects($this->exactly(2))
2795
-			->method('dispatchTyped')
2796
-			->willReturnCallback(function ($event) use (&$calls, $path): void {
2797
-				$expected = array_shift($calls);
2798
-				$this->assertInstanceOf($expected, $event);
2799
-				$share = $event->getShare();
2800
-
2801
-				$this->assertEquals(IShare::TYPE_EMAIL, $share->getShareType(), 'getShareType');
2802
-				$this->assertEquals($path, $share->getNode(), 'getNode');
2803
-				$this->assertEquals('sharedBy', $share->getSharedBy(), 'getSharedBy');
2804
-				$this->assertEquals(Constants::PERMISSION_ALL, $share->getPermissions(), 'getPermissions');
2805
-				$this->assertNull($share->getExpirationDate(), 'getExpirationDate');
2806
-				$this->assertNull($share->getPassword(), 'getPassword');
2807
-				$this->assertEquals('token', $share->getToken(), 'getToken');
2808
-
2809
-				if ($expected === ShareCreatedEvent::class) {
2810
-					$this->assertEquals('42', $share->getId(), 'getId');
2811
-					$this->assertEquals('/target', $share->getTarget(), 'getTarget');
2812
-				}
2813
-			});
2814
-
2815
-		/** @var IShare $share */
2816
-		$share = $manager->createShare($share);
2817
-
2818
-		$this->assertSame('shareOwner', $share->getShareOwner());
2819
-		$this->assertEquals('/target', $share->getTarget());
2820
-		$this->assertEquals('token', $share->getToken());
2821
-	}
2822
-
2823
-
2824
-	public function testCreateShareHookError(): void {
2825
-		$this->expectException(\Exception::class);
2826
-		$this->expectExceptionMessage('I won\'t let you share');
2827
-
2828
-		$manager = $this->createManagerMock()
2829
-			->onlyMethods([
2830
-				'canShare',
2831
-				'generalCreateChecks',
2832
-				'userCreateChecks',
2833
-				'pathCreateChecks',
2834
-			])
2835
-			->getMock();
2836
-
2837
-		$shareOwner = $this->createMock(IUser::class);
2838
-		$shareOwner->method('getUID')->willReturn('shareOwner');
2839
-
2840
-		$storage = $this->createMock(IStorage::class);
2841
-		$path = $this->createMock(File::class);
2842
-		$path->method('getOwner')->willReturn($shareOwner);
2843
-		$path->method('getName')->willReturn('target');
2844
-		$path->method('getStorage')->willReturn($storage);
2845
-
2846
-		$share = $this->createShare(
2847
-			null,
2848
-			IShare::TYPE_USER,
2849
-			$path,
2850
-			'sharedWith',
2851
-			'sharedBy',
2852
-			null,
2853
-			Constants::PERMISSION_ALL);
2854
-
2855
-		$manager->expects($this->once())
2856
-			->method('canShare')
2857
-			->with($share)
2858
-			->willReturn(true);
2859
-		$manager->expects($this->once())
2860
-			->method('generalCreateChecks')
2861
-			->with($share);
2862
-		;
2863
-		$manager->expects($this->once())
2864
-			->method('userCreateChecks')
2865
-			->with($share);
2866
-		;
2867
-		$manager->expects($this->once())
2868
-			->method('pathCreateChecks')
2869
-			->with($path);
2870
-
2871
-		$share->expects($this->once())
2872
-			->method('setShareOwner')
2873
-			->with('shareOwner');
2874
-		$share->expects($this->once())
2875
-			->method('setTarget')
2876
-			->with('/target');
2877
-
2878
-		// Pre share
2879
-		$this->dispatcher->expects($this->once())
2880
-			->method('dispatchTyped')
2881
-			->with(
2882
-				$this->isInstanceOf(BeforeShareCreatedEvent::class)
2883
-			)->willReturnCallback(function (BeforeShareCreatedEvent $e): void {
2884
-				$e->setError('I won\'t let you share!');
2885
-				$e->stopPropagation();
2886
-			}
2887
-			);
2888
-
2889
-		$manager->createShare($share);
2890
-	}
2891
-
2892
-	public function testCreateShareOfIncomingFederatedShare(): void {
2893
-		$manager = $this->createManagerMock()
2894
-			->onlyMethods(['canShare', 'generalCreateChecks', 'userCreateChecks', 'pathCreateChecks'])
2895
-			->getMock();
2896
-
2897
-		$shareOwner = $this->createMock(IUser::class);
2898
-		$shareOwner->method('getUID')->willReturn('shareOwner');
2899
-
2900
-		$storage = $this->createMock(IStorage::class);
2901
-		$storage->method('instanceOfStorage')
2902
-			->with('OCA\Files_Sharing\External\Storage')
2903
-			->willReturn(true);
2904
-
2905
-		$storage2 = $this->createMock(IStorage::class);
2906
-		$storage2->method('instanceOfStorage')
2907
-			->with('OCA\Files_Sharing\External\Storage')
2908
-			->willReturn(false);
2909
-
2910
-		$path = $this->createMock(File::class);
2911
-		$path->expects($this->never())->method('getOwner');
2912
-		$path->method('getName')->willReturn('target');
2913
-		$path->method('getStorage')->willReturn($storage);
2914
-
2915
-		$parent = $this->createMock(Folder::class);
2916
-		$parent->method('getStorage')->willReturn($storage);
2917
-
2918
-		$parentParent = $this->createMock(Folder::class);
2919
-		$parentParent->method('getStorage')->willReturn($storage2);
2920
-		$parentParent->method('getOwner')->willReturn($shareOwner);
2921
-
2922
-		$path->method('getParent')->willReturn($parent);
2923
-		$parent->method('getParent')->willReturn($parentParent);
2924
-
2925
-		$share = $this->createShare(
2926
-			null,
2927
-			IShare::TYPE_USER,
2928
-			$path,
2929
-			'sharedWith',
2930
-			'sharedBy',
2931
-			null,
2932
-			Constants::PERMISSION_ALL);
2933
-
2934
-		$manager->expects($this->once())
2935
-			->method('canShare')
2936
-			->with($share)
2937
-			->willReturn(true);
2938
-		$manager->expects($this->once())
2939
-			->method('generalCreateChecks')
2940
-			->with($share);
2941
-		;
2942
-		$manager->expects($this->once())
2943
-			->method('userCreateChecks')
2944
-			->with($share);
2945
-		;
2946
-		$manager->expects($this->once())
2947
-			->method('pathCreateChecks')
2948
-			->with($path);
2949
-
2950
-		$this->defaultProvider
2951
-			->expects($this->once())
2952
-			->method('create')
2953
-			->with($share)
2954
-			->willReturnArgument(0);
2955
-
2956
-		$share->expects($this->once())
2957
-			->method('setShareOwner')
2958
-			->with('shareOwner');
2959
-		$share->expects($this->once())
2960
-			->method('setTarget')
2961
-			->with('/target');
2962
-
2963
-		$manager->createShare($share);
2964
-	}
2965
-
2966
-	public function testGetSharesBy(): void {
2967
-		$share = $this->manager->newShare();
2968
-
2969
-		$node = $this->createMock(Folder::class);
2970
-
2971
-		$this->defaultProvider->expects($this->once())
2972
-			->method('getSharesBy')
2973
-			->with(
2974
-				$this->equalTo('user'),
2975
-				$this->equalTo(IShare::TYPE_USER),
2976
-				$this->equalTo($node),
2977
-				$this->equalTo(true),
2978
-				$this->equalTo(1),
2979
-				$this->equalTo(1)
2980
-			)->willReturn([$share]);
2981
-
2982
-		$shares = $this->manager->getSharesBy('user', IShare::TYPE_USER, $node, true, 1, 1);
2983
-
2984
-		$this->assertCount(1, $shares);
2985
-		$this->assertSame($share, $shares[0]);
2986
-	}
2987
-
2988
-	public function testGetSharesByOwnerless(): void {
2989
-		$mount = $this->createMock(IShareOwnerlessMount::class);
2990
-
2991
-		$node = $this->createMock(Folder::class);
2992
-		$node
2993
-			->expects($this->once())
2994
-			->method('getMountPoint')
2995
-			->willReturn($mount);
2996
-
2997
-		$share = $this->manager->newShare();
2998
-		$share->setNode($node);
2999
-		$share->setShareType(IShare::TYPE_USER);
3000
-
3001
-		$this->defaultProvider
3002
-			->expects($this->once())
3003
-			->method('getSharesByPath')
3004
-			->with($this->equalTo($node))
3005
-			->willReturn([$share]);
3006
-
3007
-		$shares = $this->manager->getSharesBy('user', IShare::TYPE_USER, $node, true, 1, 1);
3008
-
3009
-		$this->assertCount(1, $shares);
3010
-		$this->assertSame($share, $shares[0]);
3011
-	}
3012
-
3013
-	/**
3014
-	 * Test to ensure we correctly remove expired link shares
3015
-	 *
3016
-	 * We have 8 Shares and we want the 3 first valid shares.
3017
-	 * share 3-6 and 8 are expired. Thus at the end of this test we should
3018
-	 * have received share 1,2 and 7. And from the manager. Share 3-6 should be
3019
-	 * deleted (as they are evaluated). but share 8 should still be there.
3020
-	 */
3021
-	public function testGetSharesByExpiredLinkShares(): void {
3022
-		$manager = $this->createManagerMock()
3023
-			->onlyMethods(['deleteShare'])
3024
-			->getMock();
3025
-
3026
-		/** @var IShare[] $shares */
3027
-		$shares = [];
3028
-
3029
-		/*
2465
+        $data[] = [false, 'no', false];
2466
+        $data[] = [false, 'no', true];
2467
+        $data[] = [true, 'yes', false];
2468
+        $data[] = [false, 'yes', true];
2469
+
2470
+        return $data;
2471
+    }
2472
+
2473
+    /**
2474
+     *
2475
+     * @param bool $expected
2476
+     * @param string $sharingEnabled
2477
+     * @param bool $disabledForUser
2478
+     */
2479
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataCanShare')]
2480
+    public function testCanShare($expected, $sharingEnabled, $disabledForUser): void {
2481
+        $this->config->method('getAppValue')
2482
+            ->willReturnMap([
2483
+                ['core', 'shareapi_enabled', 'yes', $sharingEnabled],
2484
+            ]);
2485
+
2486
+        $manager = $this->createManagerMock()
2487
+            ->onlyMethods(['sharingDisabledForUser'])
2488
+            ->getMock();
2489
+
2490
+        $manager->method('sharingDisabledForUser')
2491
+            ->with('user')
2492
+            ->willReturn($disabledForUser);
2493
+
2494
+        $share = $this->manager->newShare();
2495
+        $share->setSharedBy('user');
2496
+
2497
+        $exception = false;
2498
+        try {
2499
+            $res = self::invokePrivate($manager, 'canShare', [$share]);
2500
+        } catch (\Exception $e) {
2501
+            $exception = true;
2502
+        }
2503
+
2504
+        $this->assertEquals($expected, !$exception);
2505
+    }
2506
+
2507
+    public function testCreateShareUser(): void {
2508
+        /** @var Manager&MockObject $manager */
2509
+        $manager = $this->createManagerMock()
2510
+            ->onlyMethods(['canShare', 'generalCreateChecks', 'userCreateChecks', 'pathCreateChecks'])
2511
+            ->getMock();
2512
+
2513
+        $shareOwner = $this->createMock(IUser::class);
2514
+        $shareOwner->method('getUID')->willReturn('shareOwner');
2515
+
2516
+        $storage = $this->createMock(IStorage::class);
2517
+        $path = $this->createMock(File::class);
2518
+        $path->method('getOwner')->willReturn($shareOwner);
2519
+        $path->method('getName')->willReturn('target');
2520
+        $path->method('getStorage')->willReturn($storage);
2521
+
2522
+        $share = $this->createShare(
2523
+            null,
2524
+            IShare::TYPE_USER,
2525
+            $path,
2526
+            'sharedWith',
2527
+            'sharedBy',
2528
+            null,
2529
+            Constants::PERMISSION_ALL);
2530
+
2531
+        $manager->expects($this->once())
2532
+            ->method('canShare')
2533
+            ->with($share)
2534
+            ->willReturn(true);
2535
+        $manager->expects($this->once())
2536
+            ->method('generalCreateChecks')
2537
+            ->with($share);
2538
+        ;
2539
+        $manager->expects($this->once())
2540
+            ->method('userCreateChecks')
2541
+            ->with($share);
2542
+        ;
2543
+        $manager->expects($this->once())
2544
+            ->method('pathCreateChecks')
2545
+            ->with($path);
2546
+
2547
+        $this->defaultProvider
2548
+            ->expects($this->once())
2549
+            ->method('create')
2550
+            ->with($share)
2551
+            ->willReturnArgument(0);
2552
+
2553
+        $share->expects($this->once())
2554
+            ->method('setShareOwner')
2555
+            ->with('shareOwner');
2556
+        $share->expects($this->once())
2557
+            ->method('setTarget')
2558
+            ->with('/target');
2559
+
2560
+        $manager->createShare($share);
2561
+    }
2562
+
2563
+    public function testCreateShareGroup(): void {
2564
+        $manager = $this->createManagerMock()
2565
+            ->onlyMethods(['canShare', 'generalCreateChecks', 'groupCreateChecks', 'pathCreateChecks'])
2566
+            ->getMock();
2567
+
2568
+        $shareOwner = $this->createMock(IUser::class);
2569
+        $shareOwner->method('getUID')->willReturn('shareOwner');
2570
+
2571
+        $storage = $this->createMock(IStorage::class);
2572
+        $path = $this->createMock(File::class);
2573
+        $path->method('getOwner')->willReturn($shareOwner);
2574
+        $path->method('getName')->willReturn('target');
2575
+        $path->method('getStorage')->willReturn($storage);
2576
+
2577
+        $share = $this->createShare(
2578
+            null,
2579
+            IShare::TYPE_GROUP,
2580
+            $path,
2581
+            'sharedWith',
2582
+            'sharedBy',
2583
+            null,
2584
+            Constants::PERMISSION_ALL);
2585
+
2586
+        $manager->expects($this->once())
2587
+            ->method('canShare')
2588
+            ->with($share)
2589
+            ->willReturn(true);
2590
+        $manager->expects($this->once())
2591
+            ->method('generalCreateChecks')
2592
+            ->with($share);
2593
+        ;
2594
+        $manager->expects($this->once())
2595
+            ->method('groupCreateChecks')
2596
+            ->with($share);
2597
+        ;
2598
+        $manager->expects($this->once())
2599
+            ->method('pathCreateChecks')
2600
+            ->with($path);
2601
+
2602
+        $this->defaultProvider
2603
+            ->expects($this->once())
2604
+            ->method('create')
2605
+            ->with($share)
2606
+            ->willReturnArgument(0);
2607
+
2608
+        $share->expects($this->once())
2609
+            ->method('setShareOwner')
2610
+            ->with('shareOwner');
2611
+        $share->expects($this->once())
2612
+            ->method('setTarget')
2613
+            ->with('/target');
2614
+
2615
+        $manager->createShare($share);
2616
+    }
2617
+
2618
+    public function testCreateShareLink(): void {
2619
+        $manager = $this->createManagerMock()
2620
+            ->onlyMethods([
2621
+                'canShare',
2622
+                'generalCreateChecks',
2623
+                'linkCreateChecks',
2624
+                'pathCreateChecks',
2625
+                'validateExpirationDateLink',
2626
+                'verifyPassword',
2627
+                'setLinkParent',
2628
+            ])
2629
+            ->getMock();
2630
+
2631
+        $shareOwner = $this->createMock(IUser::class);
2632
+        $shareOwner->method('getUID')->willReturn('shareOwner');
2633
+
2634
+        $storage = $this->createMock(IStorage::class);
2635
+        $path = $this->createMock(File::class);
2636
+        $path->method('getOwner')->willReturn($shareOwner);
2637
+        $path->method('getName')->willReturn('target');
2638
+        $path->method('getId')->willReturn(1);
2639
+        $path->method('getStorage')->willReturn($storage);
2640
+
2641
+        $date = new \DateTime();
2642
+
2643
+        $share = $this->manager->newShare();
2644
+        $share->setShareType(IShare::TYPE_LINK)
2645
+            ->setNode($path)
2646
+            ->setSharedBy('sharedBy')
2647
+            ->setPermissions(Constants::PERMISSION_ALL)
2648
+            ->setExpirationDate($date)
2649
+            ->setPassword('password');
2650
+
2651
+        $manager->expects($this->once())
2652
+            ->method('canShare')
2653
+            ->with($share)
2654
+            ->willReturn(true);
2655
+        $manager->expects($this->once())
2656
+            ->method('generalCreateChecks')
2657
+            ->with($share);
2658
+        ;
2659
+        $manager->expects($this->once())
2660
+            ->method('linkCreateChecks')
2661
+            ->with($share);
2662
+        ;
2663
+        $manager->expects($this->once())
2664
+            ->method('pathCreateChecks')
2665
+            ->with($path);
2666
+        $manager->expects($this->once())
2667
+            ->method('validateExpirationDateLink')
2668
+            ->with($share)
2669
+            ->willReturn($share);
2670
+        $manager->expects($this->once())
2671
+            ->method('verifyPassword')
2672
+            ->with('password');
2673
+        $manager->expects($this->once())
2674
+            ->method('setLinkParent')
2675
+            ->with($share);
2676
+
2677
+        $this->hasher->expects($this->once())
2678
+            ->method('hash')
2679
+            ->with('password')
2680
+            ->willReturn('hashed');
2681
+
2682
+        $this->secureRandom->method('generate')
2683
+            ->willReturn('token');
2684
+
2685
+        $this->defaultProvider
2686
+            ->expects($this->once())
2687
+            ->method('create')
2688
+            ->with($share)
2689
+            ->willReturnCallback(function (Share $share) {
2690
+                return $share->setId(42);
2691
+            });
2692
+
2693
+        $calls = [
2694
+            BeforeShareCreatedEvent::class,
2695
+            ShareCreatedEvent::class,
2696
+        ];
2697
+        $this->dispatcher->expects($this->exactly(2))
2698
+            ->method('dispatchTyped')
2699
+            ->willReturnCallback(function ($event) use (&$calls, $date, $path): void {
2700
+                $expected = array_shift($calls);
2701
+                $this->assertInstanceOf($expected, $event);
2702
+                $share = $event->getShare();
2703
+
2704
+                $this->assertEquals(IShare::TYPE_LINK, $share->getShareType(), 'getShareType');
2705
+                $this->assertEquals($path, $share->getNode(), 'getNode');
2706
+                $this->assertEquals('sharedBy', $share->getSharedBy(), 'getSharedBy');
2707
+                $this->assertEquals(Constants::PERMISSION_ALL, $share->getPermissions(), 'getPermissions');
2708
+                $this->assertEquals($date, $share->getExpirationDate(), 'getExpirationDate');
2709
+                $this->assertEquals('hashed', $share->getPassword(), 'getPassword');
2710
+                $this->assertEquals('token', $share->getToken(), 'getToken');
2711
+
2712
+                if ($expected === ShareCreatedEvent::class) {
2713
+                    $this->assertEquals('42', $share->getId(), 'getId');
2714
+                    $this->assertEquals('/target', $share->getTarget(), 'getTarget');
2715
+                }
2716
+            });
2717
+
2718
+        /** @var IShare $share */
2719
+        $share = $manager->createShare($share);
2720
+
2721
+        $this->assertSame('shareOwner', $share->getShareOwner());
2722
+        $this->assertEquals('/target', $share->getTarget());
2723
+        $this->assertSame($date, $share->getExpirationDate());
2724
+        $this->assertEquals('token', $share->getToken());
2725
+        $this->assertEquals('hashed', $share->getPassword());
2726
+    }
2727
+
2728
+    public function testCreateShareMail(): void {
2729
+        $manager = $this->createManagerMock()
2730
+            ->onlyMethods([
2731
+                'canShare',
2732
+                'generalCreateChecks',
2733
+                'linkCreateChecks',
2734
+                'pathCreateChecks',
2735
+                'validateExpirationDateLink',
2736
+                'verifyPassword',
2737
+                'setLinkParent',
2738
+            ])
2739
+            ->getMock();
2740
+
2741
+        $shareOwner = $this->createMock(IUser::class);
2742
+        $shareOwner->method('getUID')->willReturn('shareOwner');
2743
+
2744
+        $storage = $this->createMock(IStorage::class);
2745
+        $path = $this->createMock(File::class);
2746
+        $path->method('getOwner')->willReturn($shareOwner);
2747
+        $path->method('getName')->willReturn('target');
2748
+        $path->method('getId')->willReturn(1);
2749
+        $path->method('getStorage')->willReturn($storage);
2750
+
2751
+        $share = $this->manager->newShare();
2752
+        $share->setShareType(IShare::TYPE_EMAIL)
2753
+            ->setNode($path)
2754
+            ->setSharedBy('sharedBy')
2755
+            ->setPermissions(Constants::PERMISSION_ALL);
2756
+
2757
+        $manager->expects($this->once())
2758
+            ->method('canShare')
2759
+            ->with($share)
2760
+            ->willReturn(true);
2761
+        $manager->expects($this->once())
2762
+            ->method('generalCreateChecks')
2763
+            ->with($share);
2764
+
2765
+        $manager->expects($this->once())
2766
+            ->method('linkCreateChecks');
2767
+        $manager->expects($this->once())
2768
+            ->method('pathCreateChecks')
2769
+            ->with($path);
2770
+        $manager->expects($this->once())
2771
+            ->method('validateExpirationDateLink')
2772
+            ->with($share)
2773
+            ->willReturn($share);
2774
+        $manager->expects($this->once())
2775
+            ->method('verifyPassword');
2776
+        $manager->expects($this->once())
2777
+            ->method('setLinkParent');
2778
+
2779
+        $this->secureRandom->method('generate')
2780
+            ->willReturn('token');
2781
+
2782
+        $this->defaultProvider
2783
+            ->expects($this->once())
2784
+            ->method('create')
2785
+            ->with($share)
2786
+            ->willReturnCallback(function (Share $share) {
2787
+                return $share->setId(42);
2788
+            });
2789
+
2790
+        $calls = [
2791
+            BeforeShareCreatedEvent::class,
2792
+            ShareCreatedEvent::class,
2793
+        ];
2794
+        $this->dispatcher->expects($this->exactly(2))
2795
+            ->method('dispatchTyped')
2796
+            ->willReturnCallback(function ($event) use (&$calls, $path): void {
2797
+                $expected = array_shift($calls);
2798
+                $this->assertInstanceOf($expected, $event);
2799
+                $share = $event->getShare();
2800
+
2801
+                $this->assertEquals(IShare::TYPE_EMAIL, $share->getShareType(), 'getShareType');
2802
+                $this->assertEquals($path, $share->getNode(), 'getNode');
2803
+                $this->assertEquals('sharedBy', $share->getSharedBy(), 'getSharedBy');
2804
+                $this->assertEquals(Constants::PERMISSION_ALL, $share->getPermissions(), 'getPermissions');
2805
+                $this->assertNull($share->getExpirationDate(), 'getExpirationDate');
2806
+                $this->assertNull($share->getPassword(), 'getPassword');
2807
+                $this->assertEquals('token', $share->getToken(), 'getToken');
2808
+
2809
+                if ($expected === ShareCreatedEvent::class) {
2810
+                    $this->assertEquals('42', $share->getId(), 'getId');
2811
+                    $this->assertEquals('/target', $share->getTarget(), 'getTarget');
2812
+                }
2813
+            });
2814
+
2815
+        /** @var IShare $share */
2816
+        $share = $manager->createShare($share);
2817
+
2818
+        $this->assertSame('shareOwner', $share->getShareOwner());
2819
+        $this->assertEquals('/target', $share->getTarget());
2820
+        $this->assertEquals('token', $share->getToken());
2821
+    }
2822
+
2823
+
2824
+    public function testCreateShareHookError(): void {
2825
+        $this->expectException(\Exception::class);
2826
+        $this->expectExceptionMessage('I won\'t let you share');
2827
+
2828
+        $manager = $this->createManagerMock()
2829
+            ->onlyMethods([
2830
+                'canShare',
2831
+                'generalCreateChecks',
2832
+                'userCreateChecks',
2833
+                'pathCreateChecks',
2834
+            ])
2835
+            ->getMock();
2836
+
2837
+        $shareOwner = $this->createMock(IUser::class);
2838
+        $shareOwner->method('getUID')->willReturn('shareOwner');
2839
+
2840
+        $storage = $this->createMock(IStorage::class);
2841
+        $path = $this->createMock(File::class);
2842
+        $path->method('getOwner')->willReturn($shareOwner);
2843
+        $path->method('getName')->willReturn('target');
2844
+        $path->method('getStorage')->willReturn($storage);
2845
+
2846
+        $share = $this->createShare(
2847
+            null,
2848
+            IShare::TYPE_USER,
2849
+            $path,
2850
+            'sharedWith',
2851
+            'sharedBy',
2852
+            null,
2853
+            Constants::PERMISSION_ALL);
2854
+
2855
+        $manager->expects($this->once())
2856
+            ->method('canShare')
2857
+            ->with($share)
2858
+            ->willReturn(true);
2859
+        $manager->expects($this->once())
2860
+            ->method('generalCreateChecks')
2861
+            ->with($share);
2862
+        ;
2863
+        $manager->expects($this->once())
2864
+            ->method('userCreateChecks')
2865
+            ->with($share);
2866
+        ;
2867
+        $manager->expects($this->once())
2868
+            ->method('pathCreateChecks')
2869
+            ->with($path);
2870
+
2871
+        $share->expects($this->once())
2872
+            ->method('setShareOwner')
2873
+            ->with('shareOwner');
2874
+        $share->expects($this->once())
2875
+            ->method('setTarget')
2876
+            ->with('/target');
2877
+
2878
+        // Pre share
2879
+        $this->dispatcher->expects($this->once())
2880
+            ->method('dispatchTyped')
2881
+            ->with(
2882
+                $this->isInstanceOf(BeforeShareCreatedEvent::class)
2883
+            )->willReturnCallback(function (BeforeShareCreatedEvent $e): void {
2884
+                $e->setError('I won\'t let you share!');
2885
+                $e->stopPropagation();
2886
+            }
2887
+            );
2888
+
2889
+        $manager->createShare($share);
2890
+    }
2891
+
2892
+    public function testCreateShareOfIncomingFederatedShare(): void {
2893
+        $manager = $this->createManagerMock()
2894
+            ->onlyMethods(['canShare', 'generalCreateChecks', 'userCreateChecks', 'pathCreateChecks'])
2895
+            ->getMock();
2896
+
2897
+        $shareOwner = $this->createMock(IUser::class);
2898
+        $shareOwner->method('getUID')->willReturn('shareOwner');
2899
+
2900
+        $storage = $this->createMock(IStorage::class);
2901
+        $storage->method('instanceOfStorage')
2902
+            ->with('OCA\Files_Sharing\External\Storage')
2903
+            ->willReturn(true);
2904
+
2905
+        $storage2 = $this->createMock(IStorage::class);
2906
+        $storage2->method('instanceOfStorage')
2907
+            ->with('OCA\Files_Sharing\External\Storage')
2908
+            ->willReturn(false);
2909
+
2910
+        $path = $this->createMock(File::class);
2911
+        $path->expects($this->never())->method('getOwner');
2912
+        $path->method('getName')->willReturn('target');
2913
+        $path->method('getStorage')->willReturn($storage);
2914
+
2915
+        $parent = $this->createMock(Folder::class);
2916
+        $parent->method('getStorage')->willReturn($storage);
2917
+
2918
+        $parentParent = $this->createMock(Folder::class);
2919
+        $parentParent->method('getStorage')->willReturn($storage2);
2920
+        $parentParent->method('getOwner')->willReturn($shareOwner);
2921
+
2922
+        $path->method('getParent')->willReturn($parent);
2923
+        $parent->method('getParent')->willReturn($parentParent);
2924
+
2925
+        $share = $this->createShare(
2926
+            null,
2927
+            IShare::TYPE_USER,
2928
+            $path,
2929
+            'sharedWith',
2930
+            'sharedBy',
2931
+            null,
2932
+            Constants::PERMISSION_ALL);
2933
+
2934
+        $manager->expects($this->once())
2935
+            ->method('canShare')
2936
+            ->with($share)
2937
+            ->willReturn(true);
2938
+        $manager->expects($this->once())
2939
+            ->method('generalCreateChecks')
2940
+            ->with($share);
2941
+        ;
2942
+        $manager->expects($this->once())
2943
+            ->method('userCreateChecks')
2944
+            ->with($share);
2945
+        ;
2946
+        $manager->expects($this->once())
2947
+            ->method('pathCreateChecks')
2948
+            ->with($path);
2949
+
2950
+        $this->defaultProvider
2951
+            ->expects($this->once())
2952
+            ->method('create')
2953
+            ->with($share)
2954
+            ->willReturnArgument(0);
2955
+
2956
+        $share->expects($this->once())
2957
+            ->method('setShareOwner')
2958
+            ->with('shareOwner');
2959
+        $share->expects($this->once())
2960
+            ->method('setTarget')
2961
+            ->with('/target');
2962
+
2963
+        $manager->createShare($share);
2964
+    }
2965
+
2966
+    public function testGetSharesBy(): void {
2967
+        $share = $this->manager->newShare();
2968
+
2969
+        $node = $this->createMock(Folder::class);
2970
+
2971
+        $this->defaultProvider->expects($this->once())
2972
+            ->method('getSharesBy')
2973
+            ->with(
2974
+                $this->equalTo('user'),
2975
+                $this->equalTo(IShare::TYPE_USER),
2976
+                $this->equalTo($node),
2977
+                $this->equalTo(true),
2978
+                $this->equalTo(1),
2979
+                $this->equalTo(1)
2980
+            )->willReturn([$share]);
2981
+
2982
+        $shares = $this->manager->getSharesBy('user', IShare::TYPE_USER, $node, true, 1, 1);
2983
+
2984
+        $this->assertCount(1, $shares);
2985
+        $this->assertSame($share, $shares[0]);
2986
+    }
2987
+
2988
+    public function testGetSharesByOwnerless(): void {
2989
+        $mount = $this->createMock(IShareOwnerlessMount::class);
2990
+
2991
+        $node = $this->createMock(Folder::class);
2992
+        $node
2993
+            ->expects($this->once())
2994
+            ->method('getMountPoint')
2995
+            ->willReturn($mount);
2996
+
2997
+        $share = $this->manager->newShare();
2998
+        $share->setNode($node);
2999
+        $share->setShareType(IShare::TYPE_USER);
3000
+
3001
+        $this->defaultProvider
3002
+            ->expects($this->once())
3003
+            ->method('getSharesByPath')
3004
+            ->with($this->equalTo($node))
3005
+            ->willReturn([$share]);
3006
+
3007
+        $shares = $this->manager->getSharesBy('user', IShare::TYPE_USER, $node, true, 1, 1);
3008
+
3009
+        $this->assertCount(1, $shares);
3010
+        $this->assertSame($share, $shares[0]);
3011
+    }
3012
+
3013
+    /**
3014
+     * Test to ensure we correctly remove expired link shares
3015
+     *
3016
+     * We have 8 Shares and we want the 3 first valid shares.
3017
+     * share 3-6 and 8 are expired. Thus at the end of this test we should
3018
+     * have received share 1,2 and 7. And from the manager. Share 3-6 should be
3019
+     * deleted (as they are evaluated). but share 8 should still be there.
3020
+     */
3021
+    public function testGetSharesByExpiredLinkShares(): void {
3022
+        $manager = $this->createManagerMock()
3023
+            ->onlyMethods(['deleteShare'])
3024
+            ->getMock();
3025
+
3026
+        /** @var IShare[] $shares */
3027
+        $shares = [];
3028
+
3029
+        /*
3030 3030
 		 * This results in an array of 8 IShare elements
3031 3031
 		 */
3032
-		for ($i = 0; $i < 8; $i++) {
3033
-			$share = $this->manager->newShare();
3034
-			$share->setId($i);
3035
-			$shares[] = $share;
3036
-		}
3032
+        for ($i = 0; $i < 8; $i++) {
3033
+            $share = $this->manager->newShare();
3034
+            $share->setId($i);
3035
+            $shares[] = $share;
3036
+        }
3037 3037
 
3038
-		$today = new \DateTime();
3039
-		$today->setTime(0, 0, 0);
3038
+        $today = new \DateTime();
3039
+        $today->setTime(0, 0, 0);
3040 3040
 
3041
-		/*
3041
+        /*
3042 3042
 		 * Set the expiration date to today for some shares
3043 3043
 		 */
3044
-		$shares[2]->setExpirationDate($today);
3045
-		$shares[3]->setExpirationDate($today);
3046
-		$shares[4]->setExpirationDate($today);
3047
-		$shares[5]->setExpirationDate($today);
3044
+        $shares[2]->setExpirationDate($today);
3045
+        $shares[3]->setExpirationDate($today);
3046
+        $shares[4]->setExpirationDate($today);
3047
+        $shares[5]->setExpirationDate($today);
3048 3048
 
3049
-		/** @var IShare[] $i */
3050
-		$shares2 = [];
3051
-		for ($i = 0; $i < 8; $i++) {
3052
-			$shares2[] = clone $shares[$i];
3053
-		}
3049
+        /** @var IShare[] $i */
3050
+        $shares2 = [];
3051
+        for ($i = 0; $i < 8; $i++) {
3052
+            $shares2[] = clone $shares[$i];
3053
+        }
3054 3054
 
3055
-		$node = $this->createMock(File::class);
3055
+        $node = $this->createMock(File::class);
3056 3056
 
3057
-		/*
3057
+        /*
3058 3058
 		 * Simulate the getSharesBy call.
3059 3059
 		 */
3060
-		$this->defaultProvider
3061
-			->method('getSharesBy')
3062
-			->willReturnCallback(function ($uid, $type, $node, $reshares, $limit, $offset) use (&$shares2) {
3063
-				return array_slice($shares2, $offset, $limit);
3064
-			});
3060
+        $this->defaultProvider
3061
+            ->method('getSharesBy')
3062
+            ->willReturnCallback(function ($uid, $type, $node, $reshares, $limit, $offset) use (&$shares2) {
3063
+                return array_slice($shares2, $offset, $limit);
3064
+            });
3065 3065
 
3066
-		/*
3066
+        /*
3067 3067
 		 * Simulate the deleteShare call.
3068 3068
 		 */
3069
-		$manager->method('deleteShare')
3070
-			->willReturnCallback(function ($share) use (&$shares2): void {
3071
-				for ($i = 0; $i < count($shares2); $i++) {
3072
-					if ($shares2[$i]->getId() === $share->getId()) {
3073
-						array_splice($shares2, $i, 1);
3074
-						break;
3075
-					}
3076
-				}
3077
-			});
3078
-
3079
-		$res = $manager->getSharesBy('user', IShare::TYPE_LINK, $node, true, 3, 0);
3080
-
3081
-		$this->assertCount(3, $res);
3082
-		$this->assertEquals($shares[0]->getId(), $res[0]->getId());
3083
-		$this->assertEquals($shares[1]->getId(), $res[1]->getId());
3084
-		$this->assertEquals($shares[6]->getId(), $res[2]->getId());
3085
-
3086
-		$this->assertCount(4, $shares2);
3087
-		$this->assertEquals(0, $shares2[0]->getId());
3088
-		$this->assertEquals(1, $shares2[1]->getId());
3089
-		$this->assertEquals(6, $shares2[2]->getId());
3090
-		$this->assertEquals(7, $shares2[3]->getId());
3091
-		$this->assertSame($today, $shares[3]->getExpirationDate());
3092
-	}
3093
-
3094
-	public function testGetShareByToken(): void {
3095
-		$this->config
3096
-			->expects($this->exactly(2))
3097
-			->method('getAppValue')
3098
-			->willReturnMap([
3099
-				['core', 'shareapi_allow_links', 'yes', 'yes'],
3100
-				['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
3101
-			]);
3102
-
3103
-		$factory = $this->createMock(IProviderFactory::class);
3104
-
3105
-		$manager = $this->createManager($factory);
3106
-
3107
-		$share = $this->createMock(IShare::class);
3108
-
3109
-		$factory->expects($this->once())
3110
-			->method('getProviderForType')
3111
-			->with(IShare::TYPE_LINK)
3112
-			->willReturn($this->defaultProvider);
3113
-
3114
-		$this->defaultProvider->expects($this->once())
3115
-			->method('getShareByToken')
3116
-			->with('token')
3117
-			->willReturn($share);
3118
-
3119
-		$ret = $manager->getShareByToken('token');
3120
-		$this->assertSame($share, $ret);
3121
-	}
3122
-
3123
-	public function testGetShareByTokenRoom(): void {
3124
-		$this->config
3125
-			->expects($this->exactly(2))
3126
-			->method('getAppValue')
3127
-			->willReturnMap([
3128
-				['core', 'shareapi_allow_links', 'yes', 'no'],
3129
-				['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
3130
-			]);
3131
-
3132
-		$factory = $this->createMock(IProviderFactory::class);
3133
-
3134
-		$manager = $this->createManager($factory);
3135
-
3136
-		$share = $this->createMock(IShare::class);
3137
-
3138
-		$roomShareProvider = $this->createMock(IShareProvider::class);
3139
-
3140
-		$factory->expects($this->any())
3141
-			->method('getProviderForType')
3142
-			->willReturnCallback(function ($shareType) use ($roomShareProvider) {
3143
-				if ($shareType !== IShare::TYPE_ROOM) {
3144
-					throw new ProviderException();
3145
-				}
3146
-
3147
-				return $roomShareProvider;
3148
-			});
3149
-
3150
-		$roomShareProvider->expects($this->once())
3151
-			->method('getShareByToken')
3152
-			->with('token')
3153
-			->willReturn($share);
3154
-
3155
-		$ret = $manager->getShareByToken('token');
3156
-		$this->assertSame($share, $ret);
3157
-	}
3158
-
3159
-	public function testGetShareByTokenWithException(): void {
3160
-		$this->config
3161
-			->expects($this->exactly(2))
3162
-			->method('getAppValue')
3163
-			->willReturnMap([
3164
-				['core', 'shareapi_allow_links', 'yes', 'yes'],
3165
-				['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
3166
-			]);
3167
-
3168
-		$factory = $this->createMock(IProviderFactory::class);
3169
-
3170
-		$manager = $this->createManager($factory);
3171
-
3172
-		$share = $this->createMock(IShare::class);
3173
-
3174
-		$calls = [
3175
-			[IShare::TYPE_LINK],
3176
-			[IShare::TYPE_REMOTE],
3177
-		];
3178
-		$factory->expects($this->exactly(2))
3179
-			->method('getProviderForType')
3180
-			->willReturnCallback(function () use (&$calls) {
3181
-				$expected = array_shift($calls);
3182
-				$this->assertEquals($expected, func_get_args());
3183
-				return $this->defaultProvider;
3184
-			});
3185
-
3186
-		$this->defaultProvider->expects($this->exactly(2))
3187
-			->method('getShareByToken')
3188
-			->with('token')
3189
-			->willReturnOnConsecutiveCalls(
3190
-				$this->throwException(new ShareNotFound()),
3191
-				$share
3192
-			);
3193
-
3194
-		$ret = $manager->getShareByToken('token');
3195
-		$this->assertSame($share, $ret);
3196
-	}
3197
-
3198
-
3199
-	public function testGetShareByTokenHideDisabledUser(): void {
3200
-		$this->expectException(ShareNotFound::class);
3201
-		$this->expectExceptionMessage('The requested share comes from a disabled user');
3202
-
3203
-		$this->config
3204
-			->expects($this->exactly(2))
3205
-			->method('getAppValue')
3206
-			->willReturnMap([
3207
-				['core', 'shareapi_allow_links', 'yes', 'yes'],
3208
-				['files_sharing', 'hide_disabled_user_shares', 'no', 'yes'],
3209
-			]);
3210
-
3211
-		$this->l->expects($this->once())
3212
-			->method('t')
3213
-			->willReturnArgument(0);
3214
-
3215
-		$manager = $this->createManagerMock()
3216
-			->onlyMethods(['deleteShare'])
3217
-			->getMock();
3218
-
3219
-		$date = new \DateTime();
3220
-		$date->setTime(0, 0, 0);
3221
-		$date->add(new \DateInterval('P2D'));
3222
-		$share = $this->manager->newShare();
3223
-		$share->setExpirationDate($date);
3224
-		$share->setShareOwner('owner');
3225
-		$share->setSharedBy('sharedBy');
3226
-
3227
-		$sharedBy = $this->createMock(IUser::class);
3228
-		$owner = $this->createMock(IUser::class);
3229
-
3230
-		$this->userManager->method('get')->willReturnMap([
3231
-			['sharedBy', $sharedBy],
3232
-			['owner', $owner],
3233
-		]);
3234
-
3235
-		$owner->expects($this->once())
3236
-			->method('isEnabled')
3237
-			->willReturn(true);
3238
-		$sharedBy->expects($this->once())
3239
-			->method('isEnabled')
3240
-			->willReturn(false);
3241
-
3242
-		$this->defaultProvider->expects($this->once())
3243
-			->method('getShareByToken')
3244
-			->with('expiredToken')
3245
-			->willReturn($share);
3246
-
3247
-		$manager->expects($this->never())
3248
-			->method('deleteShare');
3249
-
3250
-		$manager->getShareByToken('expiredToken');
3251
-	}
3252
-
3253
-
3254
-	public function testGetShareByTokenExpired(): void {
3255
-		$this->expectException(ShareNotFound::class);
3256
-		$this->expectExceptionMessage('The requested share does not exist anymore');
3257
-
3258
-		$this->config
3259
-			->expects($this->once())
3260
-			->method('getAppValue')
3261
-			->with('core', 'shareapi_allow_links', 'yes')
3262
-			->willReturn('yes');
3263
-
3264
-		$this->l->expects($this->once())
3265
-			->method('t')
3266
-			->willReturnArgument(0);
3267
-
3268
-		$manager = $this->createManagerMock()
3269
-			->onlyMethods(['deleteShare'])
3270
-			->getMock();
3271
-
3272
-		$date = new \DateTime();
3273
-		$date->setTime(0, 0, 0);
3274
-		$share = $this->manager->newShare();
3275
-		$share->setExpirationDate($date);
3276
-
3277
-		$this->defaultProvider->expects($this->once())
3278
-			->method('getShareByToken')
3279
-			->with('expiredToken')
3280
-			->willReturn($share);
3281
-
3282
-		$manager->expects($this->once())
3283
-			->method('deleteShare')
3284
-			->with($this->equalTo($share));
3285
-
3286
-		$manager->getShareByToken('expiredToken');
3287
-	}
3288
-
3289
-	public function testGetShareByTokenNotExpired(): void {
3290
-		$this->config
3291
-			->expects($this->exactly(2))
3292
-			->method('getAppValue')
3293
-			->willReturnMap([
3294
-				['core', 'shareapi_allow_links', 'yes', 'yes'],
3295
-				['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
3296
-			]);
3297
-
3298
-		$date = new \DateTime();
3299
-		$date->setTime(0, 0, 0);
3300
-		$date->add(new \DateInterval('P2D'));
3301
-		$share = $this->manager->newShare();
3302
-		$share->setExpirationDate($date);
3303
-
3304
-		$this->defaultProvider->expects($this->once())
3305
-			->method('getShareByToken')
3306
-			->with('expiredToken')
3307
-			->willReturn($share);
3308
-
3309
-		$res = $this->manager->getShareByToken('expiredToken');
3310
-
3311
-		$this->assertSame($share, $res);
3312
-	}
3313
-
3314
-
3315
-	public function testGetShareByTokenWithPublicLinksDisabled(): void {
3316
-		$this->expectException(ShareNotFound::class);
3317
-
3318
-		$this->config
3319
-			->expects($this->once())
3320
-			->method('getAppValue')
3321
-			->with('core', 'shareapi_allow_links', 'yes')
3322
-			->willReturn('no');
3323
-		$this->manager->getShareByToken('validToken');
3324
-	}
3325
-
3326
-	public function testGetShareByTokenPublicUploadDisabled(): void {
3327
-		$this->config
3328
-			->expects($this->exactly(5))
3329
-			->method('getAppValue')
3330
-			->willReturnMap([
3331
-				['core', 'shareapi_allow_links', 'yes', 'yes'],
3332
-				['core', 'shareapi_allow_public_upload', 'yes', 'no'],
3333
-				['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
3334
-				['core', 'shareapi_allow_links_exclude_groups', '[]', '[]'],
3335
-			]);
3336
-
3337
-		$share = $this->manager->newShare();
3338
-		$share->setShareType(IShare::TYPE_LINK)
3339
-			->setPermissions(Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE);
3340
-		$share->setSharedWith('sharedWith');
3341
-		$share->setShareOwner('shareOwner');
3342
-		$folder = $this->createMock(\OC\Files\Node\Folder::class);
3343
-		$share->setNode($folder);
3344
-
3345
-		$shareOwner = $this->createMock(IUser::class);
3346
-		$this->userManager->expects($this->once())
3347
-			->method('get')
3348
-			->with('shareOwner')
3349
-			->willReturn($shareOwner);
3350
-
3351
-		$this->defaultProvider->expects($this->once())
3352
-			->method('getShareByToken')
3353
-			->willReturn('validToken')
3354
-			->willReturn($share);
3355
-
3356
-		$res = $this->manager->getShareByToken('validToken');
3357
-
3358
-		$this->assertSame(Constants::PERMISSION_READ, $res->getPermissions());
3359
-	}
3360
-
3361
-	public function testGetShareByTokenShareOwnerExcludedFromLinkShares(): void {
3362
-		$this->expectException(ShareNotFound::class);
3363
-		$this->expectExceptionMessage('The requested share does not exist anymore');
3364
-
3365
-		$this->config
3366
-			->expects($this->exactly(4))
3367
-			->method('getAppValue')
3368
-			->willReturnMap([
3369
-				['core', 'shareapi_allow_links', 'yes', 'yes'],
3370
-				['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
3371
-				['core', 'shareapi_allow_links_exclude_groups', '[]', '["excludedGroup"]'],
3372
-			]);
3373
-
3374
-		$this->l->expects($this->once())
3375
-			->method('t')
3376
-			->willReturnArgument(0);
3377
-
3378
-		$share = $this->manager->newShare();
3379
-		$share->setShareType(IShare::TYPE_LINK)
3380
-			->setPermissions(Constants::PERMISSION_READ);
3381
-		$share->setShareOwner('shareOwner');
3382
-		$file = $this->createMock(File::class);
3383
-		$share->setNode($file);
3384
-
3385
-		$shareOwner = $this->createMock(IUser::class);
3386
-		$this->userManager->expects($this->once())
3387
-			->method('get')
3388
-			->with('shareOwner')
3389
-			->willReturn($shareOwner);
3390
-
3391
-		$this->groupManager->expects($this->once())
3392
-			->method('getUserGroupIds')
3393
-			->with($shareOwner)
3394
-			->willReturn(['excludedGroup', 'otherGroup']);
3395
-
3396
-		$this->defaultProvider->expects($this->once())
3397
-			->method('getShareByToken')
3398
-			->with('token')
3399
-			->willReturn($share);
3400
-
3401
-		$this->manager->getShareByToken('token');
3402
-	}
3403
-
3404
-	public function testGetShareByTokenShareOwnerNotExcludedFromLinkShares(): void {
3405
-		$this->config
3406
-			->expects($this->exactly(4))
3407
-			->method('getAppValue')
3408
-			->willReturnMap([
3409
-				['core', 'shareapi_allow_links', 'yes', 'yes'],
3410
-				['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
3411
-				['core', 'shareapi_allow_links_exclude_groups', '[]', '["excludedGroup"]'],
3412
-			]);
3413
-
3414
-		$share = $this->manager->newShare();
3415
-		$share->setShareType(IShare::TYPE_LINK)
3416
-			->setPermissions(Constants::PERMISSION_READ);
3417
-		$share->setShareOwner('shareOwner');
3418
-		$file = $this->createMock(File::class);
3419
-		$share->setNode($file);
3420
-
3421
-		$shareOwner = $this->createMock(IUser::class);
3422
-		$this->userManager->expects($this->once())
3423
-			->method('get')
3424
-			->with('shareOwner')
3425
-			->willReturn($shareOwner);
3426
-
3427
-		$this->groupManager->expects($this->once())
3428
-			->method('getUserGroupIds')
3429
-			->with($shareOwner)
3430
-			->willReturn(['allowedGroup', 'otherGroup']);
3431
-
3432
-		$this->defaultProvider->expects($this->once())
3433
-			->method('getShareByToken')
3434
-			->with('token')
3435
-			->willReturn($share);
3436
-
3437
-		$res = $this->manager->getShareByToken('token');
3438
-
3439
-		$this->assertSame($share, $res);
3440
-	}
3441
-
3442
-	public function testCheckPasswordNoLinkShare(): void {
3443
-		$share = $this->createMock(IShare::class);
3444
-		$share->method('getShareType')->willReturn(IShare::TYPE_USER);
3445
-		$this->assertFalse($this->manager->checkPassword($share, 'password'));
3446
-	}
3447
-
3448
-	public function testCheckPasswordNoPassword(): void {
3449
-		$share = $this->createMock(IShare::class);
3450
-		$share->method('getShareType')->willReturn(IShare::TYPE_LINK);
3451
-		$this->assertFalse($this->manager->checkPassword($share, 'password'));
3452
-
3453
-		$share->method('getPassword')->willReturn('password');
3454
-		$this->assertFalse($this->manager->checkPassword($share, null));
3455
-	}
3456
-
3457
-	public function testCheckPasswordInvalidPassword(): void {
3458
-		$share = $this->createMock(IShare::class);
3459
-		$share->method('getShareType')->willReturn(IShare::TYPE_LINK);
3460
-		$share->method('getPassword')->willReturn('password');
3461
-
3462
-		$this->hasher->method('verify')->with('invalidpassword', 'password', '')->willReturn(false);
3463
-
3464
-		$this->assertFalse($this->manager->checkPassword($share, 'invalidpassword'));
3465
-	}
3466
-
3467
-	public function testCheckPasswordValidPassword(): void {
3468
-		$share = $this->createMock(IShare::class);
3469
-		$share->method('getShareType')->willReturn(IShare::TYPE_LINK);
3470
-		$share->method('getPassword')->willReturn('passwordHash');
3471
-
3472
-		$this->hasher->method('verify')->with('password', 'passwordHash', '')->willReturn(true);
3473
-
3474
-		$this->assertTrue($this->manager->checkPassword($share, 'password'));
3475
-	}
3476
-
3477
-	public function testCheckPasswordUpdateShare(): void {
3478
-		$share = $this->manager->newShare();
3479
-		$share->setShareType(IShare::TYPE_LINK)
3480
-			->setPassword('passwordHash');
3481
-
3482
-		$this->hasher->method('verify')->with('password', 'passwordHash', '')
3483
-			->willReturnCallback(function ($pass, $hash, &$newHash) {
3484
-				$newHash = 'newHash';
3485
-
3486
-				return true;
3487
-			});
3488
-
3489
-		$this->defaultProvider->expects($this->once())
3490
-			->method('update')
3491
-			->with($this->callback(function (IShare $share) {
3492
-				return $share->getPassword() === 'newHash';
3493
-			}));
3494
-
3495
-		$this->assertTrue($this->manager->checkPassword($share, 'password'));
3496
-	}
3497
-
3498
-
3499
-	public function testUpdateShareCantChangeShareType(): void {
3500
-		$this->expectException(\Exception::class);
3501
-		$this->expectExceptionMessage('Cannot change share type');
3502
-
3503
-		$manager = $this->createManagerMock()
3504
-			->onlyMethods([
3505
-				'canShare',
3506
-				'getShareById'
3507
-			])
3508
-			->getMock();
3509
-
3510
-		$originalShare = $this->manager->newShare();
3511
-		$originalShare->setShareType(IShare::TYPE_GROUP);
3512
-
3513
-		$manager->expects($this->once())->method('canShare')->willReturn(true);
3514
-		$manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
3515
-
3516
-		$share = $this->manager->newShare();
3517
-		$attrs = $this->manager->newShare()->newAttributes();
3518
-		$attrs->setAttribute('app1', 'perm1', true);
3519
-		$share->setProviderId('foo')
3520
-			->setId('42')
3521
-			->setShareType(IShare::TYPE_USER);
3522
-
3523
-		$manager->updateShare($share);
3524
-	}
3525
-
3526
-
3527
-	public function testUpdateShareCantChangeRecipientForGroupShare(): void {
3528
-		$this->expectException(\Exception::class);
3529
-		$this->expectExceptionMessage('Can only update recipient on user shares');
3530
-
3531
-		$manager = $this->createManagerMock()
3532
-			->onlyMethods([
3533
-				'canShare',
3534
-				'getShareById'
3535
-			])
3536
-			->getMock();
3537
-
3538
-		$originalShare = $this->manager->newShare();
3539
-		$originalShare->setShareType(IShare::TYPE_GROUP)
3540
-			->setSharedWith('origGroup');
3541
-
3542
-		$manager->expects($this->once())->method('canShare')->willReturn(true);
3543
-		$manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
3544
-
3545
-		$share = $this->manager->newShare();
3546
-		$share->setProviderId('foo')
3547
-			->setId('42')
3548
-			->setShareType(IShare::TYPE_GROUP)
3549
-			->setSharedWith('newGroup');
3550
-
3551
-		$manager->updateShare($share);
3552
-	}
3553
-
3554
-
3555
-	public function testUpdateShareCantShareWithOwner(): void {
3556
-		$this->expectException(\Exception::class);
3557
-		$this->expectExceptionMessage('Cannot share with the share owner');
3558
-
3559
-		$manager = $this->createManagerMock()
3560
-			->onlyMethods([
3561
-				'canShare',
3562
-				'getShareById'
3563
-			])
3564
-			->getMock();
3565
-
3566
-		$originalShare = $this->manager->newShare();
3567
-		$originalShare->setShareType(IShare::TYPE_USER)
3568
-			->setSharedWith('sharedWith');
3569
-
3570
-		$manager->expects($this->once())->method('canShare')->willReturn(true);
3571
-		$manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
3572
-
3573
-		$share = $this->manager->newShare();
3574
-		$share->setProviderId('foo')
3575
-			->setId('42')
3576
-			->setShareType(IShare::TYPE_USER)
3577
-			->setSharedWith('newUser')
3578
-			->setShareOwner('newUser');
3579
-
3580
-		$manager->updateShare($share);
3581
-	}
3582
-
3583
-	public function testUpdateShareUser(): void {
3584
-		$this->userManager->expects($this->any())->method('userExists')->willReturn(true);
3585
-
3586
-		$manager = $this->createManagerMock()
3587
-			->onlyMethods([
3588
-				'canShare',
3589
-				'getShareById',
3590
-				'generalCreateChecks',
3591
-				'userCreateChecks',
3592
-				'pathCreateChecks',
3593
-			])
3594
-			->getMock();
3595
-
3596
-		$originalShare = $this->manager->newShare();
3597
-		$originalShare->setShareType(IShare::TYPE_USER)
3598
-			->setSharedWith('origUser')
3599
-			->setPermissions(1);
3600
-
3601
-		$node = $this->createMock(File::class);
3602
-		$node->method('getId')->willReturn(100);
3603
-		$node->method('getPath')->willReturn('/newUser/files/myPath');
3604
-
3605
-		$manager->expects($this->once())->method('canShare')->willReturn(true);
3606
-		$manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
3607
-
3608
-		$share = $this->manager->newShare();
3609
-		$attrs = $this->manager->newShare()->newAttributes();
3610
-		$attrs->setAttribute('app1', 'perm1', true);
3611
-		$share->setProviderId('foo')
3612
-			->setId('42')
3613
-			->setShareType(IShare::TYPE_USER)
3614
-			->setSharedWith('origUser')
3615
-			->setShareOwner('newUser')
3616
-			->setSharedBy('sharer')
3617
-			->setPermissions(31)
3618
-			->setAttributes($attrs)
3619
-			->setNode($node);
3620
-
3621
-		$this->defaultProvider->expects($this->once())
3622
-			->method('update')
3623
-			->with($share)
3624
-			->willReturn($share);
3625
-
3626
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
3627
-		Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
3628
-		$hookListener->expects($this->never())->method('post');
3629
-
3630
-		$this->rootFolder->method('getUserFolder')->with('newUser')->willReturnSelf();
3631
-		$this->rootFolder->method('getRelativePath')->with('/newUser/files/myPath')->willReturn('/myPath');
3632
-
3633
-		$hookListener2 = $this->createMock(DummyShareManagerListener::class);
3634
-		Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener2, 'post');
3635
-		$hookListener2->expects($this->once())->method('post')->with([
3636
-			'itemType' => 'file',
3637
-			'itemSource' => 100,
3638
-			'shareType' => IShare::TYPE_USER,
3639
-			'shareWith' => 'origUser',
3640
-			'uidOwner' => 'sharer',
3641
-			'permissions' => 31,
3642
-			'path' => '/myPath',
3643
-			'attributes' => $attrs->toArray(),
3644
-		]);
3645
-
3646
-		$manager->updateShare($share);
3647
-	}
3648
-
3649
-	public function testUpdateShareGroup(): void {
3650
-		$manager = $this->createManagerMock()
3651
-			->onlyMethods([
3652
-				'canShare',
3653
-				'getShareById',
3654
-				'generalCreateChecks',
3655
-				'groupCreateChecks',
3656
-				'pathCreateChecks',
3657
-			])
3658
-			->getMock();
3659
-
3660
-		$originalShare = $this->manager->newShare();
3661
-		$originalShare->setShareType(IShare::TYPE_GROUP)
3662
-			->setSharedWith('origUser')
3663
-			->setPermissions(31);
3664
-
3665
-		$manager->expects($this->once())->method('canShare')->willReturn(true);
3666
-		$manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
3667
-
3668
-		$node = $this->createMock(File::class);
3669
-
3670
-		$share = $this->manager->newShare();
3671
-		$share->setProviderId('foo')
3672
-			->setId('42')
3673
-			->setShareType(IShare::TYPE_GROUP)
3674
-			->setSharedWith('origUser')
3675
-			->setShareOwner('owner')
3676
-			->setNode($node)
3677
-			->setPermissions(31);
3678
-
3679
-		$this->defaultProvider->expects($this->once())
3680
-			->method('update')
3681
-			->with($share)
3682
-			->willReturn($share);
3683
-
3684
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
3685
-		Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
3686
-		$hookListener->expects($this->never())->method('post');
3687
-
3688
-		$hookListener2 = $this->createMock(DummyShareManagerListener::class);
3689
-		Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener2, 'post');
3690
-		$hookListener2->expects($this->never())->method('post');
3691
-
3692
-		$manager->updateShare($share);
3693
-	}
3694
-
3695
-	public function testUpdateShareLink(): void {
3696
-		$manager = $this->createManagerMock()
3697
-			->onlyMethods([
3698
-				'canShare',
3699
-				'getShareById',
3700
-				'generalCreateChecks',
3701
-				'linkCreateChecks',
3702
-				'pathCreateChecks',
3703
-				'verifyPassword',
3704
-				'validateExpirationDateLink',
3705
-			])
3706
-			->getMock();
3707
-
3708
-		$originalShare = $this->manager->newShare();
3709
-		$originalShare->setShareType(IShare::TYPE_LINK)
3710
-			->setPermissions(15);
3711
-
3712
-		$tomorrow = new \DateTime();
3713
-		$tomorrow->setTime(0, 0, 0);
3714
-		$tomorrow->add(new \DateInterval('P1D'));
3715
-
3716
-		$file = $this->createMock(File::class);
3717
-		$file->method('getId')->willReturn(100);
3718
-
3719
-		$share = $this->manager->newShare();
3720
-		$share->setProviderId('foo')
3721
-			->setId('42')
3722
-			->setShareType(IShare::TYPE_LINK)
3723
-			->setToken('token')
3724
-			->setSharedBy('owner')
3725
-			->setShareOwner('owner')
3726
-			->setPassword('password')
3727
-			->setExpirationDate($tomorrow)
3728
-			->setNode($file)
3729
-			->setPermissions(15);
3730
-
3731
-		$manager->expects($this->once())->method('canShare')->willReturn(true);
3732
-		$manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
3733
-		$manager->expects($this->once())->method('validateExpirationDateLink')->with($share);
3734
-		$manager->expects($this->once())->method('verifyPassword')->with('password');
3735
-
3736
-		$this->hasher->expects($this->once())
3737
-			->method('hash')
3738
-			->with('password')
3739
-			->willReturn('hashed');
3740
-
3741
-		$this->defaultProvider->expects($this->once())
3742
-			->method('update')
3743
-			->with($share)
3744
-			->willReturn($share);
3745
-
3746
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
3747
-		Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
3748
-		$hookListener->expects($this->once())->method('post')->with([
3749
-			'itemType' => 'file',
3750
-			'itemSource' => 100,
3751
-			'date' => $tomorrow,
3752
-			'uidOwner' => 'owner',
3753
-		]);
3754
-
3755
-		$hookListener2 = $this->createMock(DummyShareManagerListener::class);
3756
-		Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
3757
-		$hookListener2->expects($this->once())->method('post')->with([
3758
-			'itemType' => 'file',
3759
-			'itemSource' => 100,
3760
-			'uidOwner' => 'owner',
3761
-			'token' => 'token',
3762
-			'disabled' => false,
3763
-		]);
3764
-
3765
-		$hookListener3 = $this->createMock(DummyShareManagerListener::class);
3766
-		Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
3767
-		$hookListener3->expects($this->never())->method('post');
3768
-
3769
-
3770
-		$manager->updateShare($share);
3771
-	}
3772
-
3773
-	public function testUpdateShareLinkEnableSendPasswordByTalkWithNoPassword(): void {
3774
-		$this->expectException(\InvalidArgumentException::class);
3775
-		$this->expectExceptionMessage('Cannot enable sending the password by Talk with an empty password');
3776
-
3777
-		$manager = $this->createManagerMock()
3778
-			->onlyMethods([
3779
-				'canShare',
3780
-				'getShareById',
3781
-				'generalCreateChecks',
3782
-				'linkCreateChecks',
3783
-				'pathCreateChecks',
3784
-				'verifyPassword',
3785
-				'validateExpirationDateLink',
3786
-			])
3787
-			->getMock();
3788
-
3789
-		$originalShare = $this->manager->newShare();
3790
-		$originalShare->setShareType(IShare::TYPE_LINK)
3791
-			->setPermissions(15);
3792
-
3793
-		$tomorrow = new \DateTime();
3794
-		$tomorrow->setTime(0, 0, 0);
3795
-		$tomorrow->add(new \DateInterval('P1D'));
3796
-
3797
-		$file = $this->createMock(File::class);
3798
-		$file->method('getId')->willReturn(100);
3799
-
3800
-		$share = $this->manager->newShare();
3801
-		$share->setProviderId('foo')
3802
-			->setId('42')
3803
-			->setShareType(IShare::TYPE_LINK)
3804
-			->setToken('token')
3805
-			->setSharedBy('owner')
3806
-			->setShareOwner('owner')
3807
-			->setPassword(null)
3808
-			->setSendPasswordByTalk(true)
3809
-			->setExpirationDate($tomorrow)
3810
-			->setNode($file)
3811
-			->setPermissions(15);
3812
-
3813
-		$manager->expects($this->once())->method('canShare')->willReturn(true);
3814
-		$manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
3815
-		$manager->expects($this->once())->method('generalCreateChecks')->with($share);
3816
-		$manager->expects($this->once())->method('linkCreateChecks')->with($share);
3817
-		$manager->expects($this->never())->method('verifyPassword');
3818
-		$manager->expects($this->never())->method('pathCreateChecks');
3819
-		$manager->expects($this->never())->method('validateExpirationDateLink');
3820
-
3821
-		$this->hasher->expects($this->never())
3822
-			->method('hash');
3823
-
3824
-		$this->defaultProvider->expects($this->never())
3825
-			->method('update');
3826
-
3827
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
3828
-		Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
3829
-		$hookListener->expects($this->never())->method('post');
3830
-
3831
-		$hookListener2 = $this->createMock(DummyShareManagerListener::class);
3832
-		Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
3833
-		$hookListener2->expects($this->never())->method('post');
3834
-
3835
-		$hookListener3 = $this->createMock(DummyShareManagerListener::class);
3836
-		Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
3837
-		$hookListener3->expects($this->never())->method('post');
3838
-
3839
-		$manager->updateShare($share);
3840
-	}
3841
-
3842
-	public function testUpdateShareMail(): void {
3843
-		$manager = $this->createManagerMock()
3844
-			->onlyMethods([
3845
-				'canShare',
3846
-				'getShareById',
3847
-				'generalCreateChecks',
3848
-				'verifyPassword',
3849
-				'pathCreateChecks',
3850
-				'linkCreateChecks',
3851
-				'validateExpirationDateLink',
3852
-			])
3853
-			->getMock();
3854
-
3855
-		$originalShare = $this->manager->newShare();
3856
-		$originalShare->setShareType(IShare::TYPE_EMAIL)
3857
-			->setPermissions(Constants::PERMISSION_ALL);
3858
-
3859
-		$tomorrow = new \DateTime();
3860
-		$tomorrow->setTime(0, 0, 0);
3861
-		$tomorrow->add(new \DateInterval('P1D'));
3862
-
3863
-		$file = $this->createMock(File::class);
3864
-		$file->method('getId')->willReturn(100);
3865
-
3866
-		$share = $this->manager->newShare();
3867
-		$share->setProviderId('foo')
3868
-			->setId('42')
3869
-			->setShareType(IShare::TYPE_EMAIL)
3870
-			->setToken('token')
3871
-			->setSharedBy('owner')
3872
-			->setShareOwner('owner')
3873
-			->setPassword('password')
3874
-			->setExpirationDate($tomorrow)
3875
-			->setNode($file)
3876
-			->setPermissions(Constants::PERMISSION_ALL);
3877
-
3878
-		$manager->expects($this->once())->method('canShare')->willReturn(true);
3879
-		$manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
3880
-		$manager->expects($this->once())->method('generalCreateChecks')->with($share);
3881
-		$manager->expects($this->once())->method('verifyPassword')->with('password');
3882
-		$manager->expects($this->once())->method('pathCreateChecks')->with($file);
3883
-		$manager->expects($this->once())->method('linkCreateChecks');
3884
-		$manager->expects($this->once())->method('validateExpirationDateLink');
3885
-
3886
-		$this->hasher->expects($this->once())
3887
-			->method('hash')
3888
-			->with('password')
3889
-			->willReturn('hashed');
3890
-
3891
-		$this->defaultProvider->expects($this->once())
3892
-			->method('update')
3893
-			->with($share, 'password')
3894
-			->willReturn($share);
3895
-
3896
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
3897
-		Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
3898
-		$hookListener->expects($this->once())->method('post')->with([
3899
-			'itemType' => 'file',
3900
-			'itemSource' => 100,
3901
-			'date' => $tomorrow,
3902
-			'uidOwner' => 'owner',
3903
-		]);
3904
-
3905
-		$hookListener2 = $this->createMock(DummyShareManagerListener::class);
3906
-		Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
3907
-		$hookListener2->expects($this->once())->method('post')->with([
3908
-			'itemType' => 'file',
3909
-			'itemSource' => 100,
3910
-			'uidOwner' => 'owner',
3911
-			'token' => 'token',
3912
-			'disabled' => false,
3913
-		]);
3914
-
3915
-		$hookListener3 = $this->createMock(DummyShareManagerListener::class);
3916
-		Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
3917
-		$hookListener3->expects($this->never())->method('post');
3918
-
3919
-		$manager->updateShare($share);
3920
-	}
3921
-
3922
-	public function testUpdateShareMailEnableSendPasswordByTalk(): void {
3923
-		$manager = $this->createManagerMock()
3924
-			->onlyMethods([
3925
-				'canShare',
3926
-				'getShareById',
3927
-				'generalCreateChecks',
3928
-				'verifyPassword',
3929
-				'pathCreateChecks',
3930
-				'linkCreateChecks',
3931
-				'validateExpirationDateLink',
3932
-			])
3933
-			->getMock();
3934
-
3935
-		$originalShare = $this->manager->newShare();
3936
-		$originalShare->setShareType(IShare::TYPE_EMAIL)
3937
-			->setPermissions(Constants::PERMISSION_ALL)
3938
-			->setPassword(null)
3939
-			->setSendPasswordByTalk(false);
3940
-
3941
-		$tomorrow = new \DateTime();
3942
-		$tomorrow->setTime(0, 0, 0);
3943
-		$tomorrow->add(new \DateInterval('P1D'));
3944
-
3945
-		$file = $this->createMock(File::class);
3946
-		$file->method('getId')->willReturn(100);
3947
-
3948
-		$share = $this->manager->newShare();
3949
-		$share->setProviderId('foo')
3950
-			->setId('42')
3951
-			->setShareType(IShare::TYPE_EMAIL)
3952
-			->setToken('token')
3953
-			->setSharedBy('owner')
3954
-			->setShareOwner('owner')
3955
-			->setPassword('password')
3956
-			->setSendPasswordByTalk(true)
3957
-			->setExpirationDate($tomorrow)
3958
-			->setNode($file)
3959
-			->setPermissions(Constants::PERMISSION_ALL);
3960
-
3961
-		$manager->expects($this->once())->method('canShare')->willReturn(true);
3962
-		$manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
3963
-		$manager->expects($this->once())->method('generalCreateChecks')->with($share);
3964
-		$manager->expects($this->once())->method('verifyPassword')->with('password');
3965
-		$manager->expects($this->once())->method('pathCreateChecks')->with($file);
3966
-		$manager->expects($this->once())->method('linkCreateChecks');
3967
-		$manager->expects($this->once())->method('validateExpirationDateLink');
3968
-
3969
-		$this->hasher->expects($this->once())
3970
-			->method('hash')
3971
-			->with('password')
3972
-			->willReturn('hashed');
3973
-
3974
-		$this->defaultProvider->expects($this->once())
3975
-			->method('update')
3976
-			->with($share, 'password')
3977
-			->willReturn($share);
3978
-
3979
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
3980
-		Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
3981
-		$hookListener->expects($this->once())->method('post')->with([
3982
-			'itemType' => 'file',
3983
-			'itemSource' => 100,
3984
-			'date' => $tomorrow,
3985
-			'uidOwner' => 'owner',
3986
-		]);
3987
-
3988
-		$hookListener2 = $this->createMock(DummyShareManagerListener::class);
3989
-		Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
3990
-		$hookListener2->expects($this->once())->method('post')->with([
3991
-			'itemType' => 'file',
3992
-			'itemSource' => 100,
3993
-			'uidOwner' => 'owner',
3994
-			'token' => 'token',
3995
-			'disabled' => false,
3996
-		]);
3997
-
3998
-		$hookListener3 = $this->createMock(DummyShareManagerListener::class);
3999
-		Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
4000
-		$hookListener3->expects($this->never())->method('post');
4001
-
4002
-		$manager->updateShare($share);
4003
-	}
4004
-
4005
-	public function testUpdateShareMailEnableSendPasswordByTalkWithDifferentPassword(): void {
4006
-		$manager = $this->createManagerMock()
4007
-			->onlyMethods([
4008
-				'canShare',
4009
-				'getShareById',
4010
-				'generalCreateChecks',
4011
-				'verifyPassword',
4012
-				'pathCreateChecks',
4013
-				'linkCreateChecks',
4014
-				'validateExpirationDateLink',
4015
-			])
4016
-			->getMock();
4017
-
4018
-		$originalShare = $this->manager->newShare();
4019
-		$originalShare->setShareType(IShare::TYPE_EMAIL)
4020
-			->setPermissions(Constants::PERMISSION_ALL)
4021
-			->setPassword('anotherPasswordHash')
4022
-			->setSendPasswordByTalk(false);
4023
-
4024
-		$tomorrow = new \DateTime();
4025
-		$tomorrow->setTime(0, 0, 0);
4026
-		$tomorrow->add(new \DateInterval('P1D'));
4027
-
4028
-		$file = $this->createMock(File::class);
4029
-		$file->method('getId')->willReturn(100);
4030
-
4031
-		$share = $this->manager->newShare();
4032
-		$share->setProviderId('foo')
4033
-			->setId('42')
4034
-			->setShareType(IShare::TYPE_EMAIL)
4035
-			->setToken('token')
4036
-			->setSharedBy('owner')
4037
-			->setShareOwner('owner')
4038
-			->setPassword('password')
4039
-			->setSendPasswordByTalk(true)
4040
-			->setExpirationDate($tomorrow)
4041
-			->setNode($file)
4042
-			->setPermissions(Constants::PERMISSION_ALL);
4043
-
4044
-		$manager->expects($this->once())->method('canShare')->willReturn(true);
4045
-		$manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
4046
-		$manager->expects($this->once())->method('generalCreateChecks')->with($share);
4047
-		$manager->expects($this->once())->method('verifyPassword')->with('password');
4048
-		$manager->expects($this->once())->method('pathCreateChecks')->with($file);
4049
-		$manager->expects($this->once())->method('linkCreateChecks');
4050
-		$manager->expects($this->once())->method('validateExpirationDateLink');
4051
-
4052
-		$this->hasher->expects($this->once())
4053
-			->method('verify')
4054
-			->with('password', 'anotherPasswordHash')
4055
-			->willReturn(false);
4056
-
4057
-		$this->hasher->expects($this->once())
4058
-			->method('hash')
4059
-			->with('password')
4060
-			->willReturn('hashed');
4061
-
4062
-		$this->defaultProvider->expects($this->once())
4063
-			->method('update')
4064
-			->with($share, 'password')
4065
-			->willReturn($share);
4066
-
4067
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
4068
-		Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
4069
-		$hookListener->expects($this->once())->method('post')->with([
4070
-			'itemType' => 'file',
4071
-			'itemSource' => 100,
4072
-			'date' => $tomorrow,
4073
-			'uidOwner' => 'owner',
4074
-		]);
4075
-
4076
-		$hookListener2 = $this->createMock(DummyShareManagerListener::class);
4077
-		Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
4078
-		$hookListener2->expects($this->once())->method('post')->with([
4079
-			'itemType' => 'file',
4080
-			'itemSource' => 100,
4081
-			'uidOwner' => 'owner',
4082
-			'token' => 'token',
4083
-			'disabled' => false,
4084
-		]);
4085
-
4086
-		$hookListener3 = $this->createMock(DummyShareManagerListener::class);
4087
-		Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
4088
-		$hookListener3->expects($this->never())->method('post');
4089
-
4090
-		$manager->updateShare($share);
4091
-	}
4092
-
4093
-	public function testUpdateShareMailEnableSendPasswordByTalkWithNoPassword(): void {
4094
-		$this->expectException(\InvalidArgumentException::class);
4095
-		$this->expectExceptionMessage('Cannot enable sending the password by Talk with an empty password');
4096
-
4097
-		$manager = $this->createManagerMock()
4098
-			->onlyMethods([
4099
-				'canShare',
4100
-				'getShareById',
4101
-				'generalCreateChecks',
4102
-				'verifyPassword',
4103
-				'pathCreateChecks',
4104
-				'linkCreateChecks',
4105
-				'validateExpirationDateLink',
4106
-			])
4107
-			->getMock();
4108
-
4109
-		$originalShare = $this->manager->newShare();
4110
-		$originalShare->setShareType(IShare::TYPE_EMAIL)
4111
-			->setPermissions(Constants::PERMISSION_ALL)
4112
-			->setPassword(null)
4113
-			->setSendPasswordByTalk(false);
4114
-
4115
-		$tomorrow = new \DateTime();
4116
-		$tomorrow->setTime(0, 0, 0);
4117
-		$tomorrow->add(new \DateInterval('P1D'));
4118
-
4119
-		$file = $this->createMock(File::class);
4120
-		$file->method('getId')->willReturn(100);
4121
-
4122
-		$share = $this->manager->newShare();
4123
-		$share->setProviderId('foo')
4124
-			->setId('42')
4125
-			->setShareType(IShare::TYPE_EMAIL)
4126
-			->setToken('token')
4127
-			->setSharedBy('owner')
4128
-			->setShareOwner('owner')
4129
-			->setPassword(null)
4130
-			->setSendPasswordByTalk(true)
4131
-			->setExpirationDate($tomorrow)
4132
-			->setNode($file)
4133
-			->setPermissions(Constants::PERMISSION_ALL);
4134
-
4135
-		$manager->expects($this->once())->method('canShare')->willReturn(true);
4136
-		$manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
4137
-		$manager->expects($this->once())->method('generalCreateChecks')->with($share);
4138
-		$manager->expects($this->never())->method('verifyPassword');
4139
-		$manager->expects($this->never())->method('pathCreateChecks');
4140
-		$manager->expects($this->once())->method('linkCreateChecks');
4141
-		$manager->expects($this->never())->method('validateExpirationDateLink');
4142
-
4143
-		// If the password is empty, we have nothing to hash
4144
-		$this->hasher->expects($this->never())
4145
-			->method('hash');
4146
-
4147
-		$this->defaultProvider->expects($this->never())
4148
-			->method('update');
4149
-
4150
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
4151
-		Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
4152
-		$hookListener->expects($this->never())->method('post');
4153
-
4154
-		$hookListener2 = $this->createMock(DummyShareManagerListener::class);
4155
-		Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
4156
-		$hookListener2->expects($this->never())->method('post');
4157
-
4158
-		$hookListener3 = $this->createMock(DummyShareManagerListener::class);
4159
-		Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
4160
-		$hookListener3->expects($this->never())->method('post');
4161
-
4162
-		$manager->updateShare($share);
4163
-	}
4164
-
4165
-
4166
-	public function testUpdateShareMailEnableSendPasswordByTalkRemovingPassword(): void {
4167
-		$this->expectException(\InvalidArgumentException::class);
4168
-		$this->expectExceptionMessage('Cannot enable sending the password by Talk with an empty password');
4169
-
4170
-		$manager = $this->createManagerMock()
4171
-			->onlyMethods([
4172
-				'canShare',
4173
-				'getShareById',
4174
-				'generalCreateChecks',
4175
-				'verifyPassword',
4176
-				'pathCreateChecks',
4177
-				'linkCreateChecks',
4178
-				'validateExpirationDateLink',
4179
-			])
4180
-			->getMock();
4181
-
4182
-		$originalShare = $this->manager->newShare();
4183
-		$originalShare->setShareType(IShare::TYPE_EMAIL)
4184
-			->setPermissions(Constants::PERMISSION_ALL)
4185
-			->setPassword('passwordHash')
4186
-			->setSendPasswordByTalk(false);
4187
-
4188
-		$tomorrow = new \DateTime();
4189
-		$tomorrow->setTime(0, 0, 0);
4190
-		$tomorrow->add(new \DateInterval('P1D'));
4191
-
4192
-		$file = $this->createMock(File::class);
4193
-		$file->method('getId')->willReturn(100);
4194
-
4195
-		$share = $this->manager->newShare();
4196
-		$share->setProviderId('foo')
4197
-			->setId('42')
4198
-			->setShareType(IShare::TYPE_EMAIL)
4199
-			->setToken('token')
4200
-			->setSharedBy('owner')
4201
-			->setShareOwner('owner')
4202
-			->setPassword(null)
4203
-			->setSendPasswordByTalk(true)
4204
-			->setExpirationDate($tomorrow)
4205
-			->setNode($file)
4206
-			->setPermissions(Constants::PERMISSION_ALL);
4207
-
4208
-		$manager->expects($this->once())->method('canShare')->willReturn(true);
4209
-		$manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
4210
-		$manager->expects($this->once())->method('generalCreateChecks')->with($share);
4211
-		$manager->expects($this->once())->method('verifyPassword');
4212
-		$manager->expects($this->never())->method('pathCreateChecks');
4213
-		$manager->expects($this->once())->method('linkCreateChecks');
4214
-		$manager->expects($this->never())->method('validateExpirationDateLink');
4215
-
4216
-		// If the password is empty, we have nothing to hash
4217
-		$this->hasher->expects($this->never())
4218
-			->method('hash');
4219
-
4220
-		$this->defaultProvider->expects($this->never())
4221
-			->method('update');
4222
-
4223
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
4224
-		Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
4225
-		$hookListener->expects($this->never())->method('post');
4226
-
4227
-		$hookListener2 = $this->createMock(DummyShareManagerListener::class);
4228
-		Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
4229
-		$hookListener2->expects($this->never())->method('post');
4230
-
4231
-		$hookListener3 = $this->createMock(DummyShareManagerListener::class);
4232
-		Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
4233
-		$hookListener3->expects($this->never())->method('post');
4234
-
4235
-		$manager->updateShare($share);
4236
-	}
4237
-
4238
-
4239
-	public function testUpdateShareMailEnableSendPasswordByTalkRemovingPasswordWithEmptyString(): void {
4240
-		$this->expectException(\InvalidArgumentException::class);
4241
-		$this->expectExceptionMessage('Cannot enable sending the password by Talk with an empty password');
4242
-
4243
-		$manager = $this->createManagerMock()
4244
-			->onlyMethods([
4245
-				'canShare',
4246
-				'getShareById',
4247
-				'generalCreateChecks',
4248
-				'verifyPassword',
4249
-				'pathCreateChecks',
4250
-				'linkCreateChecks',
4251
-				'validateExpirationDateLink',
4252
-			])
4253
-			->getMock();
4254
-
4255
-		$originalShare = $this->manager->newShare();
4256
-		$originalShare->setShareType(IShare::TYPE_EMAIL)
4257
-			->setPermissions(Constants::PERMISSION_ALL)
4258
-			->setPassword('passwordHash')
4259
-			->setSendPasswordByTalk(false);
4260
-
4261
-		$tomorrow = new \DateTime();
4262
-		$tomorrow->setTime(0, 0, 0);
4263
-		$tomorrow->add(new \DateInterval('P1D'));
4264
-
4265
-		$file = $this->createMock(File::class);
4266
-		$file->method('getId')->willReturn(100);
4267
-
4268
-		$share = $this->manager->newShare();
4269
-		$share->setProviderId('foo')
4270
-			->setId('42')
4271
-			->setShareType(IShare::TYPE_EMAIL)
4272
-			->setToken('token')
4273
-			->setSharedBy('owner')
4274
-			->setShareOwner('owner')
4275
-			->setPassword('')
4276
-			->setSendPasswordByTalk(true)
4277
-			->setExpirationDate($tomorrow)
4278
-			->setNode($file)
4279
-			->setPermissions(Constants::PERMISSION_ALL);
4280
-
4281
-		$manager->expects($this->once())->method('canShare')->willReturn(true);
4282
-		$manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
4283
-		$manager->expects($this->once())->method('generalCreateChecks')->with($share);
4284
-		$manager->expects($this->once())->method('verifyPassword');
4285
-		$manager->expects($this->never())->method('pathCreateChecks');
4286
-		$manager->expects($this->once())->method('linkCreateChecks');
4287
-		$manager->expects($this->never())->method('validateExpirationDateLink');
4288
-
4289
-		// If the password is empty, we have nothing to hash
4290
-		$this->hasher->expects($this->never())
4291
-			->method('hash');
4292
-
4293
-		$this->defaultProvider->expects($this->never())
4294
-			->method('update');
4295
-
4296
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
4297
-		Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
4298
-		$hookListener->expects($this->never())->method('post');
4299
-
4300
-		$hookListener2 = $this->createMock(DummyShareManagerListener::class);
4301
-		Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
4302
-		$hookListener2->expects($this->never())->method('post');
4303
-
4304
-		$hookListener3 = $this->createMock(DummyShareManagerListener::class);
4305
-		Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
4306
-		$hookListener3->expects($this->never())->method('post');
4307
-
4308
-		$manager->updateShare($share);
4309
-	}
4310
-
4311
-
4312
-	public function testUpdateShareMailEnableSendPasswordByTalkWithPreviousPassword(): void {
4313
-		$this->expectException(\InvalidArgumentException::class);
4314
-		$this->expectExceptionMessage('Cannot enable sending the password by Talk without setting a new password');
4315
-
4316
-		$manager = $this->createManagerMock()
4317
-			->onlyMethods([
4318
-				'canShare',
4319
-				'getShareById',
4320
-				'generalCreateChecks',
4321
-				'verifyPassword',
4322
-				'pathCreateChecks',
4323
-				'linkCreateChecks',
4324
-				'validateExpirationDateLink',
4325
-			])
4326
-			->getMock();
4327
-
4328
-		$originalShare = $this->manager->newShare();
4329
-		$originalShare->setShareType(IShare::TYPE_EMAIL)
4330
-			->setPermissions(Constants::PERMISSION_ALL)
4331
-			->setPassword('password')
4332
-			->setSendPasswordByTalk(false);
4333
-
4334
-		$tomorrow = new \DateTime();
4335
-		$tomorrow->setTime(0, 0, 0);
4336
-		$tomorrow->add(new \DateInterval('P1D'));
4337
-
4338
-		$file = $this->createMock(File::class);
4339
-		$file->method('getId')->willReturn(100);
4340
-
4341
-		$share = $this->manager->newShare();
4342
-		$share->setProviderId('foo')
4343
-			->setId('42')
4344
-			->setShareType(IShare::TYPE_EMAIL)
4345
-			->setToken('token')
4346
-			->setSharedBy('owner')
4347
-			->setShareOwner('owner')
4348
-			->setPassword('password')
4349
-			->setSendPasswordByTalk(true)
4350
-			->setExpirationDate($tomorrow)
4351
-			->setNode($file)
4352
-			->setPermissions(Constants::PERMISSION_ALL);
4353
-
4354
-		$manager->expects($this->once())->method('canShare')->willReturn(true);
4355
-		$manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
4356
-		$manager->expects($this->once())->method('generalCreateChecks')->with($share);
4357
-		$manager->expects($this->never())->method('verifyPassword');
4358
-		$manager->expects($this->never())->method('pathCreateChecks');
4359
-		$manager->expects($this->once())->method('linkCreateChecks');
4360
-		$manager->expects($this->never())->method('validateExpirationDateLink');
4361
-
4362
-		// If the old & new passwords are the same, we don't do anything
4363
-		$this->hasher->expects($this->never())
4364
-			->method('verify');
4365
-		$this->hasher->expects($this->never())
4366
-			->method('hash');
4367
-
4368
-		$this->defaultProvider->expects($this->never())
4369
-			->method('update');
4370
-
4371
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
4372
-		Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
4373
-		$hookListener->expects($this->never())->method('post');
4374
-
4375
-		$hookListener2 = $this->createMock(DummyShareManagerListener::class);
4376
-		Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
4377
-		$hookListener2->expects($this->never())->method('post');
4378
-
4379
-		$hookListener3 = $this->createMock(DummyShareManagerListener::class);
4380
-		Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
4381
-		$hookListener3->expects($this->never())->method('post');
4382
-
4383
-		$manager->updateShare($share);
4384
-	}
4385
-
4386
-	public function testUpdateShareMailDisableSendPasswordByTalkWithPreviousPassword(): void {
4387
-		$this->expectException(\InvalidArgumentException::class);
4388
-		$this->expectExceptionMessage('Cannot disable sending the password by Talk without setting a new password');
4389
-
4390
-		$manager = $this->createManagerMock()
4391
-			->onlyMethods([
4392
-				'canShare',
4393
-				'getShareById',
4394
-				'generalCreateChecks',
4395
-				'verifyPassword',
4396
-				'pathCreateChecks',
4397
-				'linkCreateChecks',
4398
-				'validateExpirationDateLink',
4399
-			])
4400
-			->getMock();
4401
-
4402
-		$originalShare = $this->manager->newShare();
4403
-		$originalShare->setShareType(IShare::TYPE_EMAIL)
4404
-			->setPermissions(Constants::PERMISSION_ALL)
4405
-			->setPassword('passwordHash')
4406
-			->setSendPasswordByTalk(true);
4407
-
4408
-		$tomorrow = new \DateTime();
4409
-		$tomorrow->setTime(0, 0, 0);
4410
-		$tomorrow->add(new \DateInterval('P1D'));
4411
-
4412
-		$file = $this->createMock(File::class);
4413
-		$file->method('getId')->willReturn(100);
4414
-
4415
-		$share = $this->manager->newShare();
4416
-		$share->setProviderId('foo')
4417
-			->setId('42')
4418
-			->setShareType(IShare::TYPE_EMAIL)
4419
-			->setToken('token')
4420
-			->setSharedBy('owner')
4421
-			->setShareOwner('owner')
4422
-			->setPassword('passwordHash')
4423
-			->setSendPasswordByTalk(false)
4424
-			->setExpirationDate($tomorrow)
4425
-			->setNode($file)
4426
-			->setPermissions(Constants::PERMISSION_ALL);
4427
-
4428
-		$manager->expects($this->once())->method('canShare')->willReturn(true);
4429
-		$manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
4430
-		$manager->expects($this->once())->method('generalCreateChecks')->with($share);
4431
-		$manager->expects($this->never())->method('verifyPassword');
4432
-		$manager->expects($this->never())->method('pathCreateChecks');
4433
-		$manager->expects($this->once())->method('linkCreateChecks');
4434
-		$manager->expects($this->never())->method('validateExpirationDateLink');
4435
-
4436
-		// If the old & new passwords are the same, we don't do anything
4437
-		$this->hasher->expects($this->never())
4438
-			->method('verify');
4439
-		$this->hasher->expects($this->never())
4440
-			->method('hash');
4441
-
4442
-		$this->defaultProvider->expects($this->never())
4443
-			->method('update');
4444
-
4445
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
4446
-		Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
4447
-		$hookListener->expects($this->never())->method('post');
4448
-
4449
-		$hookListener2 = $this->createMock(DummyShareManagerListener::class);
4450
-		Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
4451
-		$hookListener2->expects($this->never())->method('post');
4452
-
4453
-		$hookListener3 = $this->createMock(DummyShareManagerListener::class);
4454
-		Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
4455
-		$hookListener3->expects($this->never())->method('post');
4456
-
4457
-		$manager->updateShare($share);
4458
-	}
4459
-
4460
-	public function testUpdateShareMailDisableSendPasswordByTalkWithoutChangingPassword(): void {
4461
-		$this->expectException(\InvalidArgumentException::class);
4462
-		$this->expectExceptionMessage('Cannot disable sending the password by Talk without setting a new password');
4463
-
4464
-		$manager = $this->createManagerMock()
4465
-			->onlyMethods([
4466
-				'canShare',
4467
-				'getShareById',
4468
-				'generalCreateChecks',
4469
-				'verifyPassword',
4470
-				'pathCreateChecks',
4471
-				'linkCreateChecks',
4472
-				'validateExpirationDateLink',
4473
-			])
4474
-			->getMock();
4475
-
4476
-		$originalShare = $this->manager->newShare();
4477
-		$originalShare->setShareType(IShare::TYPE_EMAIL)
4478
-			->setPermissions(Constants::PERMISSION_ALL)
4479
-			->setPassword('passwordHash')
4480
-			->setSendPasswordByTalk(true);
4481
-
4482
-		$tomorrow = new \DateTime();
4483
-		$tomorrow->setTime(0, 0, 0);
4484
-		$tomorrow->add(new \DateInterval('P1D'));
4485
-
4486
-		$file = $this->createMock(File::class);
4487
-		$file->method('getId')->willReturn(100);
4488
-
4489
-		$share = $this->manager->newShare();
4490
-		$share->setProviderId('foo')
4491
-			->setId('42')
4492
-			->setShareType(IShare::TYPE_EMAIL)
4493
-			->setToken('token')
4494
-			->setSharedBy('owner')
4495
-			->setShareOwner('owner')
4496
-			->setPassword('passwordHash')
4497
-			->setSendPasswordByTalk(false)
4498
-			->setExpirationDate($tomorrow)
4499
-			->setNode($file)
4500
-			->setPermissions(Constants::PERMISSION_ALL);
4501
-
4502
-		$manager->expects($this->once())->method('canShare')->willReturn(true);
4503
-		$manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
4504
-		$manager->expects($this->once())->method('generalCreateChecks')->with($share);
4505
-		$manager->expects($this->never())->method('verifyPassword');
4506
-		$manager->expects($this->never())->method('pathCreateChecks');
4507
-		$manager->expects($this->once())->method('linkCreateChecks');
4508
-		$manager->expects($this->never())->method('validateExpirationDateLink');
4509
-
4510
-		// If the old & new passwords are the same, we don't do anything
4511
-		$this->hasher->expects($this->never())
4512
-			->method('verify');
4513
-		$this->hasher->expects($this->never())
4514
-			->method('hash');
4515
-
4516
-		$this->defaultProvider->expects($this->never())
4517
-			->method('update');
4518
-
4519
-		$hookListener = $this->createMock(DummyShareManagerListener::class);
4520
-		Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
4521
-		$hookListener->expects($this->never())->method('post');
3069
+        $manager->method('deleteShare')
3070
+            ->willReturnCallback(function ($share) use (&$shares2): void {
3071
+                for ($i = 0; $i < count($shares2); $i++) {
3072
+                    if ($shares2[$i]->getId() === $share->getId()) {
3073
+                        array_splice($shares2, $i, 1);
3074
+                        break;
3075
+                    }
3076
+                }
3077
+            });
3078
+
3079
+        $res = $manager->getSharesBy('user', IShare::TYPE_LINK, $node, true, 3, 0);
3080
+
3081
+        $this->assertCount(3, $res);
3082
+        $this->assertEquals($shares[0]->getId(), $res[0]->getId());
3083
+        $this->assertEquals($shares[1]->getId(), $res[1]->getId());
3084
+        $this->assertEquals($shares[6]->getId(), $res[2]->getId());
3085
+
3086
+        $this->assertCount(4, $shares2);
3087
+        $this->assertEquals(0, $shares2[0]->getId());
3088
+        $this->assertEquals(1, $shares2[1]->getId());
3089
+        $this->assertEquals(6, $shares2[2]->getId());
3090
+        $this->assertEquals(7, $shares2[3]->getId());
3091
+        $this->assertSame($today, $shares[3]->getExpirationDate());
3092
+    }
3093
+
3094
+    public function testGetShareByToken(): void {
3095
+        $this->config
3096
+            ->expects($this->exactly(2))
3097
+            ->method('getAppValue')
3098
+            ->willReturnMap([
3099
+                ['core', 'shareapi_allow_links', 'yes', 'yes'],
3100
+                ['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
3101
+            ]);
3102
+
3103
+        $factory = $this->createMock(IProviderFactory::class);
3104
+
3105
+        $manager = $this->createManager($factory);
3106
+
3107
+        $share = $this->createMock(IShare::class);
3108
+
3109
+        $factory->expects($this->once())
3110
+            ->method('getProviderForType')
3111
+            ->with(IShare::TYPE_LINK)
3112
+            ->willReturn($this->defaultProvider);
3113
+
3114
+        $this->defaultProvider->expects($this->once())
3115
+            ->method('getShareByToken')
3116
+            ->with('token')
3117
+            ->willReturn($share);
3118
+
3119
+        $ret = $manager->getShareByToken('token');
3120
+        $this->assertSame($share, $ret);
3121
+    }
3122
+
3123
+    public function testGetShareByTokenRoom(): void {
3124
+        $this->config
3125
+            ->expects($this->exactly(2))
3126
+            ->method('getAppValue')
3127
+            ->willReturnMap([
3128
+                ['core', 'shareapi_allow_links', 'yes', 'no'],
3129
+                ['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
3130
+            ]);
3131
+
3132
+        $factory = $this->createMock(IProviderFactory::class);
3133
+
3134
+        $manager = $this->createManager($factory);
3135
+
3136
+        $share = $this->createMock(IShare::class);
3137
+
3138
+        $roomShareProvider = $this->createMock(IShareProvider::class);
3139
+
3140
+        $factory->expects($this->any())
3141
+            ->method('getProviderForType')
3142
+            ->willReturnCallback(function ($shareType) use ($roomShareProvider) {
3143
+                if ($shareType !== IShare::TYPE_ROOM) {
3144
+                    throw new ProviderException();
3145
+                }
3146
+
3147
+                return $roomShareProvider;
3148
+            });
3149
+
3150
+        $roomShareProvider->expects($this->once())
3151
+            ->method('getShareByToken')
3152
+            ->with('token')
3153
+            ->willReturn($share);
3154
+
3155
+        $ret = $manager->getShareByToken('token');
3156
+        $this->assertSame($share, $ret);
3157
+    }
3158
+
3159
+    public function testGetShareByTokenWithException(): void {
3160
+        $this->config
3161
+            ->expects($this->exactly(2))
3162
+            ->method('getAppValue')
3163
+            ->willReturnMap([
3164
+                ['core', 'shareapi_allow_links', 'yes', 'yes'],
3165
+                ['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
3166
+            ]);
3167
+
3168
+        $factory = $this->createMock(IProviderFactory::class);
3169
+
3170
+        $manager = $this->createManager($factory);
3171
+
3172
+        $share = $this->createMock(IShare::class);
3173
+
3174
+        $calls = [
3175
+            [IShare::TYPE_LINK],
3176
+            [IShare::TYPE_REMOTE],
3177
+        ];
3178
+        $factory->expects($this->exactly(2))
3179
+            ->method('getProviderForType')
3180
+            ->willReturnCallback(function () use (&$calls) {
3181
+                $expected = array_shift($calls);
3182
+                $this->assertEquals($expected, func_get_args());
3183
+                return $this->defaultProvider;
3184
+            });
3185
+
3186
+        $this->defaultProvider->expects($this->exactly(2))
3187
+            ->method('getShareByToken')
3188
+            ->with('token')
3189
+            ->willReturnOnConsecutiveCalls(
3190
+                $this->throwException(new ShareNotFound()),
3191
+                $share
3192
+            );
3193
+
3194
+        $ret = $manager->getShareByToken('token');
3195
+        $this->assertSame($share, $ret);
3196
+    }
3197
+
3198
+
3199
+    public function testGetShareByTokenHideDisabledUser(): void {
3200
+        $this->expectException(ShareNotFound::class);
3201
+        $this->expectExceptionMessage('The requested share comes from a disabled user');
3202
+
3203
+        $this->config
3204
+            ->expects($this->exactly(2))
3205
+            ->method('getAppValue')
3206
+            ->willReturnMap([
3207
+                ['core', 'shareapi_allow_links', 'yes', 'yes'],
3208
+                ['files_sharing', 'hide_disabled_user_shares', 'no', 'yes'],
3209
+            ]);
3210
+
3211
+        $this->l->expects($this->once())
3212
+            ->method('t')
3213
+            ->willReturnArgument(0);
3214
+
3215
+        $manager = $this->createManagerMock()
3216
+            ->onlyMethods(['deleteShare'])
3217
+            ->getMock();
3218
+
3219
+        $date = new \DateTime();
3220
+        $date->setTime(0, 0, 0);
3221
+        $date->add(new \DateInterval('P2D'));
3222
+        $share = $this->manager->newShare();
3223
+        $share->setExpirationDate($date);
3224
+        $share->setShareOwner('owner');
3225
+        $share->setSharedBy('sharedBy');
3226
+
3227
+        $sharedBy = $this->createMock(IUser::class);
3228
+        $owner = $this->createMock(IUser::class);
3229
+
3230
+        $this->userManager->method('get')->willReturnMap([
3231
+            ['sharedBy', $sharedBy],
3232
+            ['owner', $owner],
3233
+        ]);
3234
+
3235
+        $owner->expects($this->once())
3236
+            ->method('isEnabled')
3237
+            ->willReturn(true);
3238
+        $sharedBy->expects($this->once())
3239
+            ->method('isEnabled')
3240
+            ->willReturn(false);
3241
+
3242
+        $this->defaultProvider->expects($this->once())
3243
+            ->method('getShareByToken')
3244
+            ->with('expiredToken')
3245
+            ->willReturn($share);
3246
+
3247
+        $manager->expects($this->never())
3248
+            ->method('deleteShare');
3249
+
3250
+        $manager->getShareByToken('expiredToken');
3251
+    }
3252
+
3253
+
3254
+    public function testGetShareByTokenExpired(): void {
3255
+        $this->expectException(ShareNotFound::class);
3256
+        $this->expectExceptionMessage('The requested share does not exist anymore');
3257
+
3258
+        $this->config
3259
+            ->expects($this->once())
3260
+            ->method('getAppValue')
3261
+            ->with('core', 'shareapi_allow_links', 'yes')
3262
+            ->willReturn('yes');
3263
+
3264
+        $this->l->expects($this->once())
3265
+            ->method('t')
3266
+            ->willReturnArgument(0);
3267
+
3268
+        $manager = $this->createManagerMock()
3269
+            ->onlyMethods(['deleteShare'])
3270
+            ->getMock();
3271
+
3272
+        $date = new \DateTime();
3273
+        $date->setTime(0, 0, 0);
3274
+        $share = $this->manager->newShare();
3275
+        $share->setExpirationDate($date);
3276
+
3277
+        $this->defaultProvider->expects($this->once())
3278
+            ->method('getShareByToken')
3279
+            ->with('expiredToken')
3280
+            ->willReturn($share);
3281
+
3282
+        $manager->expects($this->once())
3283
+            ->method('deleteShare')
3284
+            ->with($this->equalTo($share));
3285
+
3286
+        $manager->getShareByToken('expiredToken');
3287
+    }
3288
+
3289
+    public function testGetShareByTokenNotExpired(): void {
3290
+        $this->config
3291
+            ->expects($this->exactly(2))
3292
+            ->method('getAppValue')
3293
+            ->willReturnMap([
3294
+                ['core', 'shareapi_allow_links', 'yes', 'yes'],
3295
+                ['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
3296
+            ]);
3297
+
3298
+        $date = new \DateTime();
3299
+        $date->setTime(0, 0, 0);
3300
+        $date->add(new \DateInterval('P2D'));
3301
+        $share = $this->manager->newShare();
3302
+        $share->setExpirationDate($date);
3303
+
3304
+        $this->defaultProvider->expects($this->once())
3305
+            ->method('getShareByToken')
3306
+            ->with('expiredToken')
3307
+            ->willReturn($share);
3308
+
3309
+        $res = $this->manager->getShareByToken('expiredToken');
3310
+
3311
+        $this->assertSame($share, $res);
3312
+    }
3313
+
3314
+
3315
+    public function testGetShareByTokenWithPublicLinksDisabled(): void {
3316
+        $this->expectException(ShareNotFound::class);
3317
+
3318
+        $this->config
3319
+            ->expects($this->once())
3320
+            ->method('getAppValue')
3321
+            ->with('core', 'shareapi_allow_links', 'yes')
3322
+            ->willReturn('no');
3323
+        $this->manager->getShareByToken('validToken');
3324
+    }
3325
+
3326
+    public function testGetShareByTokenPublicUploadDisabled(): void {
3327
+        $this->config
3328
+            ->expects($this->exactly(5))
3329
+            ->method('getAppValue')
3330
+            ->willReturnMap([
3331
+                ['core', 'shareapi_allow_links', 'yes', 'yes'],
3332
+                ['core', 'shareapi_allow_public_upload', 'yes', 'no'],
3333
+                ['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
3334
+                ['core', 'shareapi_allow_links_exclude_groups', '[]', '[]'],
3335
+            ]);
3336
+
3337
+        $share = $this->manager->newShare();
3338
+        $share->setShareType(IShare::TYPE_LINK)
3339
+            ->setPermissions(Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE);
3340
+        $share->setSharedWith('sharedWith');
3341
+        $share->setShareOwner('shareOwner');
3342
+        $folder = $this->createMock(\OC\Files\Node\Folder::class);
3343
+        $share->setNode($folder);
3344
+
3345
+        $shareOwner = $this->createMock(IUser::class);
3346
+        $this->userManager->expects($this->once())
3347
+            ->method('get')
3348
+            ->with('shareOwner')
3349
+            ->willReturn($shareOwner);
3350
+
3351
+        $this->defaultProvider->expects($this->once())
3352
+            ->method('getShareByToken')
3353
+            ->willReturn('validToken')
3354
+            ->willReturn($share);
3355
+
3356
+        $res = $this->manager->getShareByToken('validToken');
3357
+
3358
+        $this->assertSame(Constants::PERMISSION_READ, $res->getPermissions());
3359
+    }
3360
+
3361
+    public function testGetShareByTokenShareOwnerExcludedFromLinkShares(): void {
3362
+        $this->expectException(ShareNotFound::class);
3363
+        $this->expectExceptionMessage('The requested share does not exist anymore');
3364
+
3365
+        $this->config
3366
+            ->expects($this->exactly(4))
3367
+            ->method('getAppValue')
3368
+            ->willReturnMap([
3369
+                ['core', 'shareapi_allow_links', 'yes', 'yes'],
3370
+                ['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
3371
+                ['core', 'shareapi_allow_links_exclude_groups', '[]', '["excludedGroup"]'],
3372
+            ]);
3373
+
3374
+        $this->l->expects($this->once())
3375
+            ->method('t')
3376
+            ->willReturnArgument(0);
3377
+
3378
+        $share = $this->manager->newShare();
3379
+        $share->setShareType(IShare::TYPE_LINK)
3380
+            ->setPermissions(Constants::PERMISSION_READ);
3381
+        $share->setShareOwner('shareOwner');
3382
+        $file = $this->createMock(File::class);
3383
+        $share->setNode($file);
3384
+
3385
+        $shareOwner = $this->createMock(IUser::class);
3386
+        $this->userManager->expects($this->once())
3387
+            ->method('get')
3388
+            ->with('shareOwner')
3389
+            ->willReturn($shareOwner);
3390
+
3391
+        $this->groupManager->expects($this->once())
3392
+            ->method('getUserGroupIds')
3393
+            ->with($shareOwner)
3394
+            ->willReturn(['excludedGroup', 'otherGroup']);
3395
+
3396
+        $this->defaultProvider->expects($this->once())
3397
+            ->method('getShareByToken')
3398
+            ->with('token')
3399
+            ->willReturn($share);
3400
+
3401
+        $this->manager->getShareByToken('token');
3402
+    }
3403
+
3404
+    public function testGetShareByTokenShareOwnerNotExcludedFromLinkShares(): void {
3405
+        $this->config
3406
+            ->expects($this->exactly(4))
3407
+            ->method('getAppValue')
3408
+            ->willReturnMap([
3409
+                ['core', 'shareapi_allow_links', 'yes', 'yes'],
3410
+                ['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
3411
+                ['core', 'shareapi_allow_links_exclude_groups', '[]', '["excludedGroup"]'],
3412
+            ]);
3413
+
3414
+        $share = $this->manager->newShare();
3415
+        $share->setShareType(IShare::TYPE_LINK)
3416
+            ->setPermissions(Constants::PERMISSION_READ);
3417
+        $share->setShareOwner('shareOwner');
3418
+        $file = $this->createMock(File::class);
3419
+        $share->setNode($file);
3420
+
3421
+        $shareOwner = $this->createMock(IUser::class);
3422
+        $this->userManager->expects($this->once())
3423
+            ->method('get')
3424
+            ->with('shareOwner')
3425
+            ->willReturn($shareOwner);
3426
+
3427
+        $this->groupManager->expects($this->once())
3428
+            ->method('getUserGroupIds')
3429
+            ->with($shareOwner)
3430
+            ->willReturn(['allowedGroup', 'otherGroup']);
3431
+
3432
+        $this->defaultProvider->expects($this->once())
3433
+            ->method('getShareByToken')
3434
+            ->with('token')
3435
+            ->willReturn($share);
3436
+
3437
+        $res = $this->manager->getShareByToken('token');
3438
+
3439
+        $this->assertSame($share, $res);
3440
+    }
3441
+
3442
+    public function testCheckPasswordNoLinkShare(): void {
3443
+        $share = $this->createMock(IShare::class);
3444
+        $share->method('getShareType')->willReturn(IShare::TYPE_USER);
3445
+        $this->assertFalse($this->manager->checkPassword($share, 'password'));
3446
+    }
3447
+
3448
+    public function testCheckPasswordNoPassword(): void {
3449
+        $share = $this->createMock(IShare::class);
3450
+        $share->method('getShareType')->willReturn(IShare::TYPE_LINK);
3451
+        $this->assertFalse($this->manager->checkPassword($share, 'password'));
3452
+
3453
+        $share->method('getPassword')->willReturn('password');
3454
+        $this->assertFalse($this->manager->checkPassword($share, null));
3455
+    }
3456
+
3457
+    public function testCheckPasswordInvalidPassword(): void {
3458
+        $share = $this->createMock(IShare::class);
3459
+        $share->method('getShareType')->willReturn(IShare::TYPE_LINK);
3460
+        $share->method('getPassword')->willReturn('password');
3461
+
3462
+        $this->hasher->method('verify')->with('invalidpassword', 'password', '')->willReturn(false);
3463
+
3464
+        $this->assertFalse($this->manager->checkPassword($share, 'invalidpassword'));
3465
+    }
3466
+
3467
+    public function testCheckPasswordValidPassword(): void {
3468
+        $share = $this->createMock(IShare::class);
3469
+        $share->method('getShareType')->willReturn(IShare::TYPE_LINK);
3470
+        $share->method('getPassword')->willReturn('passwordHash');
3471
+
3472
+        $this->hasher->method('verify')->with('password', 'passwordHash', '')->willReturn(true);
3473
+
3474
+        $this->assertTrue($this->manager->checkPassword($share, 'password'));
3475
+    }
3476
+
3477
+    public function testCheckPasswordUpdateShare(): void {
3478
+        $share = $this->manager->newShare();
3479
+        $share->setShareType(IShare::TYPE_LINK)
3480
+            ->setPassword('passwordHash');
3481
+
3482
+        $this->hasher->method('verify')->with('password', 'passwordHash', '')
3483
+            ->willReturnCallback(function ($pass, $hash, &$newHash) {
3484
+                $newHash = 'newHash';
3485
+
3486
+                return true;
3487
+            });
3488
+
3489
+        $this->defaultProvider->expects($this->once())
3490
+            ->method('update')
3491
+            ->with($this->callback(function (IShare $share) {
3492
+                return $share->getPassword() === 'newHash';
3493
+            }));
3494
+
3495
+        $this->assertTrue($this->manager->checkPassword($share, 'password'));
3496
+    }
3497
+
3498
+
3499
+    public function testUpdateShareCantChangeShareType(): void {
3500
+        $this->expectException(\Exception::class);
3501
+        $this->expectExceptionMessage('Cannot change share type');
3502
+
3503
+        $manager = $this->createManagerMock()
3504
+            ->onlyMethods([
3505
+                'canShare',
3506
+                'getShareById'
3507
+            ])
3508
+            ->getMock();
3509
+
3510
+        $originalShare = $this->manager->newShare();
3511
+        $originalShare->setShareType(IShare::TYPE_GROUP);
3512
+
3513
+        $manager->expects($this->once())->method('canShare')->willReturn(true);
3514
+        $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
3515
+
3516
+        $share = $this->manager->newShare();
3517
+        $attrs = $this->manager->newShare()->newAttributes();
3518
+        $attrs->setAttribute('app1', 'perm1', true);
3519
+        $share->setProviderId('foo')
3520
+            ->setId('42')
3521
+            ->setShareType(IShare::TYPE_USER);
3522
+
3523
+        $manager->updateShare($share);
3524
+    }
3525
+
3526
+
3527
+    public function testUpdateShareCantChangeRecipientForGroupShare(): void {
3528
+        $this->expectException(\Exception::class);
3529
+        $this->expectExceptionMessage('Can only update recipient on user shares');
3530
+
3531
+        $manager = $this->createManagerMock()
3532
+            ->onlyMethods([
3533
+                'canShare',
3534
+                'getShareById'
3535
+            ])
3536
+            ->getMock();
3537
+
3538
+        $originalShare = $this->manager->newShare();
3539
+        $originalShare->setShareType(IShare::TYPE_GROUP)
3540
+            ->setSharedWith('origGroup');
3541
+
3542
+        $manager->expects($this->once())->method('canShare')->willReturn(true);
3543
+        $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
3544
+
3545
+        $share = $this->manager->newShare();
3546
+        $share->setProviderId('foo')
3547
+            ->setId('42')
3548
+            ->setShareType(IShare::TYPE_GROUP)
3549
+            ->setSharedWith('newGroup');
3550
+
3551
+        $manager->updateShare($share);
3552
+    }
3553
+
3554
+
3555
+    public function testUpdateShareCantShareWithOwner(): void {
3556
+        $this->expectException(\Exception::class);
3557
+        $this->expectExceptionMessage('Cannot share with the share owner');
3558
+
3559
+        $manager = $this->createManagerMock()
3560
+            ->onlyMethods([
3561
+                'canShare',
3562
+                'getShareById'
3563
+            ])
3564
+            ->getMock();
3565
+
3566
+        $originalShare = $this->manager->newShare();
3567
+        $originalShare->setShareType(IShare::TYPE_USER)
3568
+            ->setSharedWith('sharedWith');
3569
+
3570
+        $manager->expects($this->once())->method('canShare')->willReturn(true);
3571
+        $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
3572
+
3573
+        $share = $this->manager->newShare();
3574
+        $share->setProviderId('foo')
3575
+            ->setId('42')
3576
+            ->setShareType(IShare::TYPE_USER)
3577
+            ->setSharedWith('newUser')
3578
+            ->setShareOwner('newUser');
3579
+
3580
+        $manager->updateShare($share);
3581
+    }
3582
+
3583
+    public function testUpdateShareUser(): void {
3584
+        $this->userManager->expects($this->any())->method('userExists')->willReturn(true);
3585
+
3586
+        $manager = $this->createManagerMock()
3587
+            ->onlyMethods([
3588
+                'canShare',
3589
+                'getShareById',
3590
+                'generalCreateChecks',
3591
+                'userCreateChecks',
3592
+                'pathCreateChecks',
3593
+            ])
3594
+            ->getMock();
3595
+
3596
+        $originalShare = $this->manager->newShare();
3597
+        $originalShare->setShareType(IShare::TYPE_USER)
3598
+            ->setSharedWith('origUser')
3599
+            ->setPermissions(1);
3600
+
3601
+        $node = $this->createMock(File::class);
3602
+        $node->method('getId')->willReturn(100);
3603
+        $node->method('getPath')->willReturn('/newUser/files/myPath');
3604
+
3605
+        $manager->expects($this->once())->method('canShare')->willReturn(true);
3606
+        $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
3607
+
3608
+        $share = $this->manager->newShare();
3609
+        $attrs = $this->manager->newShare()->newAttributes();
3610
+        $attrs->setAttribute('app1', 'perm1', true);
3611
+        $share->setProviderId('foo')
3612
+            ->setId('42')
3613
+            ->setShareType(IShare::TYPE_USER)
3614
+            ->setSharedWith('origUser')
3615
+            ->setShareOwner('newUser')
3616
+            ->setSharedBy('sharer')
3617
+            ->setPermissions(31)
3618
+            ->setAttributes($attrs)
3619
+            ->setNode($node);
3620
+
3621
+        $this->defaultProvider->expects($this->once())
3622
+            ->method('update')
3623
+            ->with($share)
3624
+            ->willReturn($share);
3625
+
3626
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
3627
+        Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
3628
+        $hookListener->expects($this->never())->method('post');
3629
+
3630
+        $this->rootFolder->method('getUserFolder')->with('newUser')->willReturnSelf();
3631
+        $this->rootFolder->method('getRelativePath')->with('/newUser/files/myPath')->willReturn('/myPath');
3632
+
3633
+        $hookListener2 = $this->createMock(DummyShareManagerListener::class);
3634
+        Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener2, 'post');
3635
+        $hookListener2->expects($this->once())->method('post')->with([
3636
+            'itemType' => 'file',
3637
+            'itemSource' => 100,
3638
+            'shareType' => IShare::TYPE_USER,
3639
+            'shareWith' => 'origUser',
3640
+            'uidOwner' => 'sharer',
3641
+            'permissions' => 31,
3642
+            'path' => '/myPath',
3643
+            'attributes' => $attrs->toArray(),
3644
+        ]);
3645
+
3646
+        $manager->updateShare($share);
3647
+    }
3648
+
3649
+    public function testUpdateShareGroup(): void {
3650
+        $manager = $this->createManagerMock()
3651
+            ->onlyMethods([
3652
+                'canShare',
3653
+                'getShareById',
3654
+                'generalCreateChecks',
3655
+                'groupCreateChecks',
3656
+                'pathCreateChecks',
3657
+            ])
3658
+            ->getMock();
3659
+
3660
+        $originalShare = $this->manager->newShare();
3661
+        $originalShare->setShareType(IShare::TYPE_GROUP)
3662
+            ->setSharedWith('origUser')
3663
+            ->setPermissions(31);
3664
+
3665
+        $manager->expects($this->once())->method('canShare')->willReturn(true);
3666
+        $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
3667
+
3668
+        $node = $this->createMock(File::class);
3669
+
3670
+        $share = $this->manager->newShare();
3671
+        $share->setProviderId('foo')
3672
+            ->setId('42')
3673
+            ->setShareType(IShare::TYPE_GROUP)
3674
+            ->setSharedWith('origUser')
3675
+            ->setShareOwner('owner')
3676
+            ->setNode($node)
3677
+            ->setPermissions(31);
3678
+
3679
+        $this->defaultProvider->expects($this->once())
3680
+            ->method('update')
3681
+            ->with($share)
3682
+            ->willReturn($share);
3683
+
3684
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
3685
+        Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
3686
+        $hookListener->expects($this->never())->method('post');
3687
+
3688
+        $hookListener2 = $this->createMock(DummyShareManagerListener::class);
3689
+        Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener2, 'post');
3690
+        $hookListener2->expects($this->never())->method('post');
3691
+
3692
+        $manager->updateShare($share);
3693
+    }
3694
+
3695
+    public function testUpdateShareLink(): void {
3696
+        $manager = $this->createManagerMock()
3697
+            ->onlyMethods([
3698
+                'canShare',
3699
+                'getShareById',
3700
+                'generalCreateChecks',
3701
+                'linkCreateChecks',
3702
+                'pathCreateChecks',
3703
+                'verifyPassword',
3704
+                'validateExpirationDateLink',
3705
+            ])
3706
+            ->getMock();
3707
+
3708
+        $originalShare = $this->manager->newShare();
3709
+        $originalShare->setShareType(IShare::TYPE_LINK)
3710
+            ->setPermissions(15);
3711
+
3712
+        $tomorrow = new \DateTime();
3713
+        $tomorrow->setTime(0, 0, 0);
3714
+        $tomorrow->add(new \DateInterval('P1D'));
3715
+
3716
+        $file = $this->createMock(File::class);
3717
+        $file->method('getId')->willReturn(100);
3718
+
3719
+        $share = $this->manager->newShare();
3720
+        $share->setProviderId('foo')
3721
+            ->setId('42')
3722
+            ->setShareType(IShare::TYPE_LINK)
3723
+            ->setToken('token')
3724
+            ->setSharedBy('owner')
3725
+            ->setShareOwner('owner')
3726
+            ->setPassword('password')
3727
+            ->setExpirationDate($tomorrow)
3728
+            ->setNode($file)
3729
+            ->setPermissions(15);
3730
+
3731
+        $manager->expects($this->once())->method('canShare')->willReturn(true);
3732
+        $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
3733
+        $manager->expects($this->once())->method('validateExpirationDateLink')->with($share);
3734
+        $manager->expects($this->once())->method('verifyPassword')->with('password');
3735
+
3736
+        $this->hasher->expects($this->once())
3737
+            ->method('hash')
3738
+            ->with('password')
3739
+            ->willReturn('hashed');
3740
+
3741
+        $this->defaultProvider->expects($this->once())
3742
+            ->method('update')
3743
+            ->with($share)
3744
+            ->willReturn($share);
3745
+
3746
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
3747
+        Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
3748
+        $hookListener->expects($this->once())->method('post')->with([
3749
+            'itemType' => 'file',
3750
+            'itemSource' => 100,
3751
+            'date' => $tomorrow,
3752
+            'uidOwner' => 'owner',
3753
+        ]);
3754
+
3755
+        $hookListener2 = $this->createMock(DummyShareManagerListener::class);
3756
+        Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
3757
+        $hookListener2->expects($this->once())->method('post')->with([
3758
+            'itemType' => 'file',
3759
+            'itemSource' => 100,
3760
+            'uidOwner' => 'owner',
3761
+            'token' => 'token',
3762
+            'disabled' => false,
3763
+        ]);
3764
+
3765
+        $hookListener3 = $this->createMock(DummyShareManagerListener::class);
3766
+        Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
3767
+        $hookListener3->expects($this->never())->method('post');
3768
+
3769
+
3770
+        $manager->updateShare($share);
3771
+    }
3772
+
3773
+    public function testUpdateShareLinkEnableSendPasswordByTalkWithNoPassword(): void {
3774
+        $this->expectException(\InvalidArgumentException::class);
3775
+        $this->expectExceptionMessage('Cannot enable sending the password by Talk with an empty password');
3776
+
3777
+        $manager = $this->createManagerMock()
3778
+            ->onlyMethods([
3779
+                'canShare',
3780
+                'getShareById',
3781
+                'generalCreateChecks',
3782
+                'linkCreateChecks',
3783
+                'pathCreateChecks',
3784
+                'verifyPassword',
3785
+                'validateExpirationDateLink',
3786
+            ])
3787
+            ->getMock();
3788
+
3789
+        $originalShare = $this->manager->newShare();
3790
+        $originalShare->setShareType(IShare::TYPE_LINK)
3791
+            ->setPermissions(15);
3792
+
3793
+        $tomorrow = new \DateTime();
3794
+        $tomorrow->setTime(0, 0, 0);
3795
+        $tomorrow->add(new \DateInterval('P1D'));
3796
+
3797
+        $file = $this->createMock(File::class);
3798
+        $file->method('getId')->willReturn(100);
3799
+
3800
+        $share = $this->manager->newShare();
3801
+        $share->setProviderId('foo')
3802
+            ->setId('42')
3803
+            ->setShareType(IShare::TYPE_LINK)
3804
+            ->setToken('token')
3805
+            ->setSharedBy('owner')
3806
+            ->setShareOwner('owner')
3807
+            ->setPassword(null)
3808
+            ->setSendPasswordByTalk(true)
3809
+            ->setExpirationDate($tomorrow)
3810
+            ->setNode($file)
3811
+            ->setPermissions(15);
3812
+
3813
+        $manager->expects($this->once())->method('canShare')->willReturn(true);
3814
+        $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
3815
+        $manager->expects($this->once())->method('generalCreateChecks')->with($share);
3816
+        $manager->expects($this->once())->method('linkCreateChecks')->with($share);
3817
+        $manager->expects($this->never())->method('verifyPassword');
3818
+        $manager->expects($this->never())->method('pathCreateChecks');
3819
+        $manager->expects($this->never())->method('validateExpirationDateLink');
3820
+
3821
+        $this->hasher->expects($this->never())
3822
+            ->method('hash');
3823
+
3824
+        $this->defaultProvider->expects($this->never())
3825
+            ->method('update');
3826
+
3827
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
3828
+        Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
3829
+        $hookListener->expects($this->never())->method('post');
3830
+
3831
+        $hookListener2 = $this->createMock(DummyShareManagerListener::class);
3832
+        Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
3833
+        $hookListener2->expects($this->never())->method('post');
3834
+
3835
+        $hookListener3 = $this->createMock(DummyShareManagerListener::class);
3836
+        Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
3837
+        $hookListener3->expects($this->never())->method('post');
3838
+
3839
+        $manager->updateShare($share);
3840
+    }
3841
+
3842
+    public function testUpdateShareMail(): void {
3843
+        $manager = $this->createManagerMock()
3844
+            ->onlyMethods([
3845
+                'canShare',
3846
+                'getShareById',
3847
+                'generalCreateChecks',
3848
+                'verifyPassword',
3849
+                'pathCreateChecks',
3850
+                'linkCreateChecks',
3851
+                'validateExpirationDateLink',
3852
+            ])
3853
+            ->getMock();
3854
+
3855
+        $originalShare = $this->manager->newShare();
3856
+        $originalShare->setShareType(IShare::TYPE_EMAIL)
3857
+            ->setPermissions(Constants::PERMISSION_ALL);
3858
+
3859
+        $tomorrow = new \DateTime();
3860
+        $tomorrow->setTime(0, 0, 0);
3861
+        $tomorrow->add(new \DateInterval('P1D'));
3862
+
3863
+        $file = $this->createMock(File::class);
3864
+        $file->method('getId')->willReturn(100);
3865
+
3866
+        $share = $this->manager->newShare();
3867
+        $share->setProviderId('foo')
3868
+            ->setId('42')
3869
+            ->setShareType(IShare::TYPE_EMAIL)
3870
+            ->setToken('token')
3871
+            ->setSharedBy('owner')
3872
+            ->setShareOwner('owner')
3873
+            ->setPassword('password')
3874
+            ->setExpirationDate($tomorrow)
3875
+            ->setNode($file)
3876
+            ->setPermissions(Constants::PERMISSION_ALL);
3877
+
3878
+        $manager->expects($this->once())->method('canShare')->willReturn(true);
3879
+        $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
3880
+        $manager->expects($this->once())->method('generalCreateChecks')->with($share);
3881
+        $manager->expects($this->once())->method('verifyPassword')->with('password');
3882
+        $manager->expects($this->once())->method('pathCreateChecks')->with($file);
3883
+        $manager->expects($this->once())->method('linkCreateChecks');
3884
+        $manager->expects($this->once())->method('validateExpirationDateLink');
3885
+
3886
+        $this->hasher->expects($this->once())
3887
+            ->method('hash')
3888
+            ->with('password')
3889
+            ->willReturn('hashed');
3890
+
3891
+        $this->defaultProvider->expects($this->once())
3892
+            ->method('update')
3893
+            ->with($share, 'password')
3894
+            ->willReturn($share);
3895
+
3896
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
3897
+        Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
3898
+        $hookListener->expects($this->once())->method('post')->with([
3899
+            'itemType' => 'file',
3900
+            'itemSource' => 100,
3901
+            'date' => $tomorrow,
3902
+            'uidOwner' => 'owner',
3903
+        ]);
3904
+
3905
+        $hookListener2 = $this->createMock(DummyShareManagerListener::class);
3906
+        Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
3907
+        $hookListener2->expects($this->once())->method('post')->with([
3908
+            'itemType' => 'file',
3909
+            'itemSource' => 100,
3910
+            'uidOwner' => 'owner',
3911
+            'token' => 'token',
3912
+            'disabled' => false,
3913
+        ]);
3914
+
3915
+        $hookListener3 = $this->createMock(DummyShareManagerListener::class);
3916
+        Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
3917
+        $hookListener3->expects($this->never())->method('post');
3918
+
3919
+        $manager->updateShare($share);
3920
+    }
3921
+
3922
+    public function testUpdateShareMailEnableSendPasswordByTalk(): void {
3923
+        $manager = $this->createManagerMock()
3924
+            ->onlyMethods([
3925
+                'canShare',
3926
+                'getShareById',
3927
+                'generalCreateChecks',
3928
+                'verifyPassword',
3929
+                'pathCreateChecks',
3930
+                'linkCreateChecks',
3931
+                'validateExpirationDateLink',
3932
+            ])
3933
+            ->getMock();
3934
+
3935
+        $originalShare = $this->manager->newShare();
3936
+        $originalShare->setShareType(IShare::TYPE_EMAIL)
3937
+            ->setPermissions(Constants::PERMISSION_ALL)
3938
+            ->setPassword(null)
3939
+            ->setSendPasswordByTalk(false);
3940
+
3941
+        $tomorrow = new \DateTime();
3942
+        $tomorrow->setTime(0, 0, 0);
3943
+        $tomorrow->add(new \DateInterval('P1D'));
3944
+
3945
+        $file = $this->createMock(File::class);
3946
+        $file->method('getId')->willReturn(100);
3947
+
3948
+        $share = $this->manager->newShare();
3949
+        $share->setProviderId('foo')
3950
+            ->setId('42')
3951
+            ->setShareType(IShare::TYPE_EMAIL)
3952
+            ->setToken('token')
3953
+            ->setSharedBy('owner')
3954
+            ->setShareOwner('owner')
3955
+            ->setPassword('password')
3956
+            ->setSendPasswordByTalk(true)
3957
+            ->setExpirationDate($tomorrow)
3958
+            ->setNode($file)
3959
+            ->setPermissions(Constants::PERMISSION_ALL);
3960
+
3961
+        $manager->expects($this->once())->method('canShare')->willReturn(true);
3962
+        $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
3963
+        $manager->expects($this->once())->method('generalCreateChecks')->with($share);
3964
+        $manager->expects($this->once())->method('verifyPassword')->with('password');
3965
+        $manager->expects($this->once())->method('pathCreateChecks')->with($file);
3966
+        $manager->expects($this->once())->method('linkCreateChecks');
3967
+        $manager->expects($this->once())->method('validateExpirationDateLink');
3968
+
3969
+        $this->hasher->expects($this->once())
3970
+            ->method('hash')
3971
+            ->with('password')
3972
+            ->willReturn('hashed');
3973
+
3974
+        $this->defaultProvider->expects($this->once())
3975
+            ->method('update')
3976
+            ->with($share, 'password')
3977
+            ->willReturn($share);
3978
+
3979
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
3980
+        Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
3981
+        $hookListener->expects($this->once())->method('post')->with([
3982
+            'itemType' => 'file',
3983
+            'itemSource' => 100,
3984
+            'date' => $tomorrow,
3985
+            'uidOwner' => 'owner',
3986
+        ]);
3987
+
3988
+        $hookListener2 = $this->createMock(DummyShareManagerListener::class);
3989
+        Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
3990
+        $hookListener2->expects($this->once())->method('post')->with([
3991
+            'itemType' => 'file',
3992
+            'itemSource' => 100,
3993
+            'uidOwner' => 'owner',
3994
+            'token' => 'token',
3995
+            'disabled' => false,
3996
+        ]);
3997
+
3998
+        $hookListener3 = $this->createMock(DummyShareManagerListener::class);
3999
+        Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
4000
+        $hookListener3->expects($this->never())->method('post');
4001
+
4002
+        $manager->updateShare($share);
4003
+    }
4004
+
4005
+    public function testUpdateShareMailEnableSendPasswordByTalkWithDifferentPassword(): void {
4006
+        $manager = $this->createManagerMock()
4007
+            ->onlyMethods([
4008
+                'canShare',
4009
+                'getShareById',
4010
+                'generalCreateChecks',
4011
+                'verifyPassword',
4012
+                'pathCreateChecks',
4013
+                'linkCreateChecks',
4014
+                'validateExpirationDateLink',
4015
+            ])
4016
+            ->getMock();
4017
+
4018
+        $originalShare = $this->manager->newShare();
4019
+        $originalShare->setShareType(IShare::TYPE_EMAIL)
4020
+            ->setPermissions(Constants::PERMISSION_ALL)
4021
+            ->setPassword('anotherPasswordHash')
4022
+            ->setSendPasswordByTalk(false);
4023
+
4024
+        $tomorrow = new \DateTime();
4025
+        $tomorrow->setTime(0, 0, 0);
4026
+        $tomorrow->add(new \DateInterval('P1D'));
4027
+
4028
+        $file = $this->createMock(File::class);
4029
+        $file->method('getId')->willReturn(100);
4030
+
4031
+        $share = $this->manager->newShare();
4032
+        $share->setProviderId('foo')
4033
+            ->setId('42')
4034
+            ->setShareType(IShare::TYPE_EMAIL)
4035
+            ->setToken('token')
4036
+            ->setSharedBy('owner')
4037
+            ->setShareOwner('owner')
4038
+            ->setPassword('password')
4039
+            ->setSendPasswordByTalk(true)
4040
+            ->setExpirationDate($tomorrow)
4041
+            ->setNode($file)
4042
+            ->setPermissions(Constants::PERMISSION_ALL);
4043
+
4044
+        $manager->expects($this->once())->method('canShare')->willReturn(true);
4045
+        $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
4046
+        $manager->expects($this->once())->method('generalCreateChecks')->with($share);
4047
+        $manager->expects($this->once())->method('verifyPassword')->with('password');
4048
+        $manager->expects($this->once())->method('pathCreateChecks')->with($file);
4049
+        $manager->expects($this->once())->method('linkCreateChecks');
4050
+        $manager->expects($this->once())->method('validateExpirationDateLink');
4051
+
4052
+        $this->hasher->expects($this->once())
4053
+            ->method('verify')
4054
+            ->with('password', 'anotherPasswordHash')
4055
+            ->willReturn(false);
4056
+
4057
+        $this->hasher->expects($this->once())
4058
+            ->method('hash')
4059
+            ->with('password')
4060
+            ->willReturn('hashed');
4061
+
4062
+        $this->defaultProvider->expects($this->once())
4063
+            ->method('update')
4064
+            ->with($share, 'password')
4065
+            ->willReturn($share);
4066
+
4067
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
4068
+        Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
4069
+        $hookListener->expects($this->once())->method('post')->with([
4070
+            'itemType' => 'file',
4071
+            'itemSource' => 100,
4072
+            'date' => $tomorrow,
4073
+            'uidOwner' => 'owner',
4074
+        ]);
4075
+
4076
+        $hookListener2 = $this->createMock(DummyShareManagerListener::class);
4077
+        Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
4078
+        $hookListener2->expects($this->once())->method('post')->with([
4079
+            'itemType' => 'file',
4080
+            'itemSource' => 100,
4081
+            'uidOwner' => 'owner',
4082
+            'token' => 'token',
4083
+            'disabled' => false,
4084
+        ]);
4085
+
4086
+        $hookListener3 = $this->createMock(DummyShareManagerListener::class);
4087
+        Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
4088
+        $hookListener3->expects($this->never())->method('post');
4089
+
4090
+        $manager->updateShare($share);
4091
+    }
4092
+
4093
+    public function testUpdateShareMailEnableSendPasswordByTalkWithNoPassword(): void {
4094
+        $this->expectException(\InvalidArgumentException::class);
4095
+        $this->expectExceptionMessage('Cannot enable sending the password by Talk with an empty password');
4096
+
4097
+        $manager = $this->createManagerMock()
4098
+            ->onlyMethods([
4099
+                'canShare',
4100
+                'getShareById',
4101
+                'generalCreateChecks',
4102
+                'verifyPassword',
4103
+                'pathCreateChecks',
4104
+                'linkCreateChecks',
4105
+                'validateExpirationDateLink',
4106
+            ])
4107
+            ->getMock();
4108
+
4109
+        $originalShare = $this->manager->newShare();
4110
+        $originalShare->setShareType(IShare::TYPE_EMAIL)
4111
+            ->setPermissions(Constants::PERMISSION_ALL)
4112
+            ->setPassword(null)
4113
+            ->setSendPasswordByTalk(false);
4114
+
4115
+        $tomorrow = new \DateTime();
4116
+        $tomorrow->setTime(0, 0, 0);
4117
+        $tomorrow->add(new \DateInterval('P1D'));
4118
+
4119
+        $file = $this->createMock(File::class);
4120
+        $file->method('getId')->willReturn(100);
4121
+
4122
+        $share = $this->manager->newShare();
4123
+        $share->setProviderId('foo')
4124
+            ->setId('42')
4125
+            ->setShareType(IShare::TYPE_EMAIL)
4126
+            ->setToken('token')
4127
+            ->setSharedBy('owner')
4128
+            ->setShareOwner('owner')
4129
+            ->setPassword(null)
4130
+            ->setSendPasswordByTalk(true)
4131
+            ->setExpirationDate($tomorrow)
4132
+            ->setNode($file)
4133
+            ->setPermissions(Constants::PERMISSION_ALL);
4134
+
4135
+        $manager->expects($this->once())->method('canShare')->willReturn(true);
4136
+        $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
4137
+        $manager->expects($this->once())->method('generalCreateChecks')->with($share);
4138
+        $manager->expects($this->never())->method('verifyPassword');
4139
+        $manager->expects($this->never())->method('pathCreateChecks');
4140
+        $manager->expects($this->once())->method('linkCreateChecks');
4141
+        $manager->expects($this->never())->method('validateExpirationDateLink');
4142
+
4143
+        // If the password is empty, we have nothing to hash
4144
+        $this->hasher->expects($this->never())
4145
+            ->method('hash');
4146
+
4147
+        $this->defaultProvider->expects($this->never())
4148
+            ->method('update');
4149
+
4150
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
4151
+        Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
4152
+        $hookListener->expects($this->never())->method('post');
4153
+
4154
+        $hookListener2 = $this->createMock(DummyShareManagerListener::class);
4155
+        Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
4156
+        $hookListener2->expects($this->never())->method('post');
4157
+
4158
+        $hookListener3 = $this->createMock(DummyShareManagerListener::class);
4159
+        Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
4160
+        $hookListener3->expects($this->never())->method('post');
4161
+
4162
+        $manager->updateShare($share);
4163
+    }
4164
+
4165
+
4166
+    public function testUpdateShareMailEnableSendPasswordByTalkRemovingPassword(): void {
4167
+        $this->expectException(\InvalidArgumentException::class);
4168
+        $this->expectExceptionMessage('Cannot enable sending the password by Talk with an empty password');
4169
+
4170
+        $manager = $this->createManagerMock()
4171
+            ->onlyMethods([
4172
+                'canShare',
4173
+                'getShareById',
4174
+                'generalCreateChecks',
4175
+                'verifyPassword',
4176
+                'pathCreateChecks',
4177
+                'linkCreateChecks',
4178
+                'validateExpirationDateLink',
4179
+            ])
4180
+            ->getMock();
4181
+
4182
+        $originalShare = $this->manager->newShare();
4183
+        $originalShare->setShareType(IShare::TYPE_EMAIL)
4184
+            ->setPermissions(Constants::PERMISSION_ALL)
4185
+            ->setPassword('passwordHash')
4186
+            ->setSendPasswordByTalk(false);
4187
+
4188
+        $tomorrow = new \DateTime();
4189
+        $tomorrow->setTime(0, 0, 0);
4190
+        $tomorrow->add(new \DateInterval('P1D'));
4191
+
4192
+        $file = $this->createMock(File::class);
4193
+        $file->method('getId')->willReturn(100);
4194
+
4195
+        $share = $this->manager->newShare();
4196
+        $share->setProviderId('foo')
4197
+            ->setId('42')
4198
+            ->setShareType(IShare::TYPE_EMAIL)
4199
+            ->setToken('token')
4200
+            ->setSharedBy('owner')
4201
+            ->setShareOwner('owner')
4202
+            ->setPassword(null)
4203
+            ->setSendPasswordByTalk(true)
4204
+            ->setExpirationDate($tomorrow)
4205
+            ->setNode($file)
4206
+            ->setPermissions(Constants::PERMISSION_ALL);
4207
+
4208
+        $manager->expects($this->once())->method('canShare')->willReturn(true);
4209
+        $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
4210
+        $manager->expects($this->once())->method('generalCreateChecks')->with($share);
4211
+        $manager->expects($this->once())->method('verifyPassword');
4212
+        $manager->expects($this->never())->method('pathCreateChecks');
4213
+        $manager->expects($this->once())->method('linkCreateChecks');
4214
+        $manager->expects($this->never())->method('validateExpirationDateLink');
4215
+
4216
+        // If the password is empty, we have nothing to hash
4217
+        $this->hasher->expects($this->never())
4218
+            ->method('hash');
4219
+
4220
+        $this->defaultProvider->expects($this->never())
4221
+            ->method('update');
4222
+
4223
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
4224
+        Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
4225
+        $hookListener->expects($this->never())->method('post');
4226
+
4227
+        $hookListener2 = $this->createMock(DummyShareManagerListener::class);
4228
+        Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
4229
+        $hookListener2->expects($this->never())->method('post');
4230
+
4231
+        $hookListener3 = $this->createMock(DummyShareManagerListener::class);
4232
+        Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
4233
+        $hookListener3->expects($this->never())->method('post');
4234
+
4235
+        $manager->updateShare($share);
4236
+    }
4237
+
4238
+
4239
+    public function testUpdateShareMailEnableSendPasswordByTalkRemovingPasswordWithEmptyString(): void {
4240
+        $this->expectException(\InvalidArgumentException::class);
4241
+        $this->expectExceptionMessage('Cannot enable sending the password by Talk with an empty password');
4242
+
4243
+        $manager = $this->createManagerMock()
4244
+            ->onlyMethods([
4245
+                'canShare',
4246
+                'getShareById',
4247
+                'generalCreateChecks',
4248
+                'verifyPassword',
4249
+                'pathCreateChecks',
4250
+                'linkCreateChecks',
4251
+                'validateExpirationDateLink',
4252
+            ])
4253
+            ->getMock();
4254
+
4255
+        $originalShare = $this->manager->newShare();
4256
+        $originalShare->setShareType(IShare::TYPE_EMAIL)
4257
+            ->setPermissions(Constants::PERMISSION_ALL)
4258
+            ->setPassword('passwordHash')
4259
+            ->setSendPasswordByTalk(false);
4260
+
4261
+        $tomorrow = new \DateTime();
4262
+        $tomorrow->setTime(0, 0, 0);
4263
+        $tomorrow->add(new \DateInterval('P1D'));
4264
+
4265
+        $file = $this->createMock(File::class);
4266
+        $file->method('getId')->willReturn(100);
4267
+
4268
+        $share = $this->manager->newShare();
4269
+        $share->setProviderId('foo')
4270
+            ->setId('42')
4271
+            ->setShareType(IShare::TYPE_EMAIL)
4272
+            ->setToken('token')
4273
+            ->setSharedBy('owner')
4274
+            ->setShareOwner('owner')
4275
+            ->setPassword('')
4276
+            ->setSendPasswordByTalk(true)
4277
+            ->setExpirationDate($tomorrow)
4278
+            ->setNode($file)
4279
+            ->setPermissions(Constants::PERMISSION_ALL);
4280
+
4281
+        $manager->expects($this->once())->method('canShare')->willReturn(true);
4282
+        $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
4283
+        $manager->expects($this->once())->method('generalCreateChecks')->with($share);
4284
+        $manager->expects($this->once())->method('verifyPassword');
4285
+        $manager->expects($this->never())->method('pathCreateChecks');
4286
+        $manager->expects($this->once())->method('linkCreateChecks');
4287
+        $manager->expects($this->never())->method('validateExpirationDateLink');
4288
+
4289
+        // If the password is empty, we have nothing to hash
4290
+        $this->hasher->expects($this->never())
4291
+            ->method('hash');
4292
+
4293
+        $this->defaultProvider->expects($this->never())
4294
+            ->method('update');
4295
+
4296
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
4297
+        Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
4298
+        $hookListener->expects($this->never())->method('post');
4299
+
4300
+        $hookListener2 = $this->createMock(DummyShareManagerListener::class);
4301
+        Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
4302
+        $hookListener2->expects($this->never())->method('post');
4303
+
4304
+        $hookListener3 = $this->createMock(DummyShareManagerListener::class);
4305
+        Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
4306
+        $hookListener3->expects($this->never())->method('post');
4307
+
4308
+        $manager->updateShare($share);
4309
+    }
4310
+
4311
+
4312
+    public function testUpdateShareMailEnableSendPasswordByTalkWithPreviousPassword(): void {
4313
+        $this->expectException(\InvalidArgumentException::class);
4314
+        $this->expectExceptionMessage('Cannot enable sending the password by Talk without setting a new password');
4315
+
4316
+        $manager = $this->createManagerMock()
4317
+            ->onlyMethods([
4318
+                'canShare',
4319
+                'getShareById',
4320
+                'generalCreateChecks',
4321
+                'verifyPassword',
4322
+                'pathCreateChecks',
4323
+                'linkCreateChecks',
4324
+                'validateExpirationDateLink',
4325
+            ])
4326
+            ->getMock();
4327
+
4328
+        $originalShare = $this->manager->newShare();
4329
+        $originalShare->setShareType(IShare::TYPE_EMAIL)
4330
+            ->setPermissions(Constants::PERMISSION_ALL)
4331
+            ->setPassword('password')
4332
+            ->setSendPasswordByTalk(false);
4333
+
4334
+        $tomorrow = new \DateTime();
4335
+        $tomorrow->setTime(0, 0, 0);
4336
+        $tomorrow->add(new \DateInterval('P1D'));
4337
+
4338
+        $file = $this->createMock(File::class);
4339
+        $file->method('getId')->willReturn(100);
4340
+
4341
+        $share = $this->manager->newShare();
4342
+        $share->setProviderId('foo')
4343
+            ->setId('42')
4344
+            ->setShareType(IShare::TYPE_EMAIL)
4345
+            ->setToken('token')
4346
+            ->setSharedBy('owner')
4347
+            ->setShareOwner('owner')
4348
+            ->setPassword('password')
4349
+            ->setSendPasswordByTalk(true)
4350
+            ->setExpirationDate($tomorrow)
4351
+            ->setNode($file)
4352
+            ->setPermissions(Constants::PERMISSION_ALL);
4353
+
4354
+        $manager->expects($this->once())->method('canShare')->willReturn(true);
4355
+        $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
4356
+        $manager->expects($this->once())->method('generalCreateChecks')->with($share);
4357
+        $manager->expects($this->never())->method('verifyPassword');
4358
+        $manager->expects($this->never())->method('pathCreateChecks');
4359
+        $manager->expects($this->once())->method('linkCreateChecks');
4360
+        $manager->expects($this->never())->method('validateExpirationDateLink');
4361
+
4362
+        // If the old & new passwords are the same, we don't do anything
4363
+        $this->hasher->expects($this->never())
4364
+            ->method('verify');
4365
+        $this->hasher->expects($this->never())
4366
+            ->method('hash');
4367
+
4368
+        $this->defaultProvider->expects($this->never())
4369
+            ->method('update');
4370
+
4371
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
4372
+        Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
4373
+        $hookListener->expects($this->never())->method('post');
4374
+
4375
+        $hookListener2 = $this->createMock(DummyShareManagerListener::class);
4376
+        Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
4377
+        $hookListener2->expects($this->never())->method('post');
4378
+
4379
+        $hookListener3 = $this->createMock(DummyShareManagerListener::class);
4380
+        Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
4381
+        $hookListener3->expects($this->never())->method('post');
4382
+
4383
+        $manager->updateShare($share);
4384
+    }
4385
+
4386
+    public function testUpdateShareMailDisableSendPasswordByTalkWithPreviousPassword(): void {
4387
+        $this->expectException(\InvalidArgumentException::class);
4388
+        $this->expectExceptionMessage('Cannot disable sending the password by Talk without setting a new password');
4389
+
4390
+        $manager = $this->createManagerMock()
4391
+            ->onlyMethods([
4392
+                'canShare',
4393
+                'getShareById',
4394
+                'generalCreateChecks',
4395
+                'verifyPassword',
4396
+                'pathCreateChecks',
4397
+                'linkCreateChecks',
4398
+                'validateExpirationDateLink',
4399
+            ])
4400
+            ->getMock();
4401
+
4402
+        $originalShare = $this->manager->newShare();
4403
+        $originalShare->setShareType(IShare::TYPE_EMAIL)
4404
+            ->setPermissions(Constants::PERMISSION_ALL)
4405
+            ->setPassword('passwordHash')
4406
+            ->setSendPasswordByTalk(true);
4407
+
4408
+        $tomorrow = new \DateTime();
4409
+        $tomorrow->setTime(0, 0, 0);
4410
+        $tomorrow->add(new \DateInterval('P1D'));
4411
+
4412
+        $file = $this->createMock(File::class);
4413
+        $file->method('getId')->willReturn(100);
4414
+
4415
+        $share = $this->manager->newShare();
4416
+        $share->setProviderId('foo')
4417
+            ->setId('42')
4418
+            ->setShareType(IShare::TYPE_EMAIL)
4419
+            ->setToken('token')
4420
+            ->setSharedBy('owner')
4421
+            ->setShareOwner('owner')
4422
+            ->setPassword('passwordHash')
4423
+            ->setSendPasswordByTalk(false)
4424
+            ->setExpirationDate($tomorrow)
4425
+            ->setNode($file)
4426
+            ->setPermissions(Constants::PERMISSION_ALL);
4427
+
4428
+        $manager->expects($this->once())->method('canShare')->willReturn(true);
4429
+        $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
4430
+        $manager->expects($this->once())->method('generalCreateChecks')->with($share);
4431
+        $manager->expects($this->never())->method('verifyPassword');
4432
+        $manager->expects($this->never())->method('pathCreateChecks');
4433
+        $manager->expects($this->once())->method('linkCreateChecks');
4434
+        $manager->expects($this->never())->method('validateExpirationDateLink');
4435
+
4436
+        // If the old & new passwords are the same, we don't do anything
4437
+        $this->hasher->expects($this->never())
4438
+            ->method('verify');
4439
+        $this->hasher->expects($this->never())
4440
+            ->method('hash');
4441
+
4442
+        $this->defaultProvider->expects($this->never())
4443
+            ->method('update');
4444
+
4445
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
4446
+        Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
4447
+        $hookListener->expects($this->never())->method('post');
4448
+
4449
+        $hookListener2 = $this->createMock(DummyShareManagerListener::class);
4450
+        Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
4451
+        $hookListener2->expects($this->never())->method('post');
4452
+
4453
+        $hookListener3 = $this->createMock(DummyShareManagerListener::class);
4454
+        Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
4455
+        $hookListener3->expects($this->never())->method('post');
4456
+
4457
+        $manager->updateShare($share);
4458
+    }
4459
+
4460
+    public function testUpdateShareMailDisableSendPasswordByTalkWithoutChangingPassword(): void {
4461
+        $this->expectException(\InvalidArgumentException::class);
4462
+        $this->expectExceptionMessage('Cannot disable sending the password by Talk without setting a new password');
4463
+
4464
+        $manager = $this->createManagerMock()
4465
+            ->onlyMethods([
4466
+                'canShare',
4467
+                'getShareById',
4468
+                'generalCreateChecks',
4469
+                'verifyPassword',
4470
+                'pathCreateChecks',
4471
+                'linkCreateChecks',
4472
+                'validateExpirationDateLink',
4473
+            ])
4474
+            ->getMock();
4475
+
4476
+        $originalShare = $this->manager->newShare();
4477
+        $originalShare->setShareType(IShare::TYPE_EMAIL)
4478
+            ->setPermissions(Constants::PERMISSION_ALL)
4479
+            ->setPassword('passwordHash')
4480
+            ->setSendPasswordByTalk(true);
4481
+
4482
+        $tomorrow = new \DateTime();
4483
+        $tomorrow->setTime(0, 0, 0);
4484
+        $tomorrow->add(new \DateInterval('P1D'));
4485
+
4486
+        $file = $this->createMock(File::class);
4487
+        $file->method('getId')->willReturn(100);
4488
+
4489
+        $share = $this->manager->newShare();
4490
+        $share->setProviderId('foo')
4491
+            ->setId('42')
4492
+            ->setShareType(IShare::TYPE_EMAIL)
4493
+            ->setToken('token')
4494
+            ->setSharedBy('owner')
4495
+            ->setShareOwner('owner')
4496
+            ->setPassword('passwordHash')
4497
+            ->setSendPasswordByTalk(false)
4498
+            ->setExpirationDate($tomorrow)
4499
+            ->setNode($file)
4500
+            ->setPermissions(Constants::PERMISSION_ALL);
4501
+
4502
+        $manager->expects($this->once())->method('canShare')->willReturn(true);
4503
+        $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
4504
+        $manager->expects($this->once())->method('generalCreateChecks')->with($share);
4505
+        $manager->expects($this->never())->method('verifyPassword');
4506
+        $manager->expects($this->never())->method('pathCreateChecks');
4507
+        $manager->expects($this->once())->method('linkCreateChecks');
4508
+        $manager->expects($this->never())->method('validateExpirationDateLink');
4509
+
4510
+        // If the old & new passwords are the same, we don't do anything
4511
+        $this->hasher->expects($this->never())
4512
+            ->method('verify');
4513
+        $this->hasher->expects($this->never())
4514
+            ->method('hash');
4515
+
4516
+        $this->defaultProvider->expects($this->never())
4517
+            ->method('update');
4518
+
4519
+        $hookListener = $this->createMock(DummyShareManagerListener::class);
4520
+        Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
4521
+        $hookListener->expects($this->never())->method('post');
4522 4522
 
4523
-		$hookListener2 = $this->createMock(DummyShareManagerListener::class);
4524
-		Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
4525
-		$hookListener2->expects($this->never())->method('post');
4523
+        $hookListener2 = $this->createMock(DummyShareManagerListener::class);
4524
+        Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
4525
+        $hookListener2->expects($this->never())->method('post');
4526 4526
 
4527
-		$hookListener3 = $this->createMock(DummyShareManagerListener::class);
4528
-		Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
4529
-		$hookListener3->expects($this->never())->method('post');
4527
+        $hookListener3 = $this->createMock(DummyShareManagerListener::class);
4528
+        Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
4529
+        $hookListener3->expects($this->never())->method('post');
4530 4530
 
4531
-		$manager->updateShare($share);
4532
-	}
4531
+        $manager->updateShare($share);
4532
+    }
4533 4533
 
4534
-	public function testMoveShareLink(): void {
4535
-		$this->expectException(\InvalidArgumentException::class);
4536
-		$this->expectExceptionMessage('Cannot change target of link share');
4534
+    public function testMoveShareLink(): void {
4535
+        $this->expectException(\InvalidArgumentException::class);
4536
+        $this->expectExceptionMessage('Cannot change target of link share');
4537 4537
 
4538
-		$share = $this->manager->newShare();
4539
-		$share->setShareType(IShare::TYPE_LINK);
4538
+        $share = $this->manager->newShare();
4539
+        $share->setShareType(IShare::TYPE_LINK);
4540 4540
 
4541
-		$recipient = $this->createMock(IUser::class);
4541
+        $recipient = $this->createMock(IUser::class);
4542 4542
 
4543
-		$this->manager->moveShare($share, $recipient);
4544
-	}
4543
+        $this->manager->moveShare($share, $recipient);
4544
+    }
4545 4545
 
4546 4546
 
4547
-	public function testMoveShareUserNotRecipient(): void {
4548
-		$this->expectException(\InvalidArgumentException::class);
4549
-		$this->expectExceptionMessage('Invalid share recipient');
4547
+    public function testMoveShareUserNotRecipient(): void {
4548
+        $this->expectException(\InvalidArgumentException::class);
4549
+        $this->expectExceptionMessage('Invalid share recipient');
4550 4550
 
4551
-		$share = $this->manager->newShare();
4552
-		$share->setShareType(IShare::TYPE_USER);
4553
-
4554
-		$share->setSharedWith('sharedWith');
4551
+        $share = $this->manager->newShare();
4552
+        $share->setShareType(IShare::TYPE_USER);
4553
+
4554
+        $share->setSharedWith('sharedWith');
4555 4555
 
4556
-		$this->manager->moveShare($share, 'recipient');
4557
-	}
4556
+        $this->manager->moveShare($share, 'recipient');
4557
+    }
4558 4558
 
4559
-	public function testMoveShareUser(): void {
4560
-		$share = $this->manager->newShare();
4561
-		$share->setShareType(IShare::TYPE_USER)
4562
-			->setId('42')
4563
-			->setProviderId('foo');
4559
+    public function testMoveShareUser(): void {
4560
+        $share = $this->manager->newShare();
4561
+        $share->setShareType(IShare::TYPE_USER)
4562
+            ->setId('42')
4563
+            ->setProviderId('foo');
4564 4564
 
4565
-		$share->setSharedWith('recipient');
4565
+        $share->setSharedWith('recipient');
4566 4566
 
4567
-		$this->defaultProvider->method('move')->with($share, 'recipient')->willReturnArgument(0);
4567
+        $this->defaultProvider->method('move')->with($share, 'recipient')->willReturnArgument(0);
4568 4568
 
4569
-		$this->manager->moveShare($share, 'recipient');
4570
-		$this->addToAssertionCount(1);
4571
-	}
4569
+        $this->manager->moveShare($share, 'recipient');
4570
+        $this->addToAssertionCount(1);
4571
+    }
4572 4572
 
4573 4573
 
4574
-	public function testMoveShareGroupNotRecipient(): void {
4575
-		$this->expectException(\InvalidArgumentException::class);
4576
-		$this->expectExceptionMessage('Invalid share recipient');
4574
+    public function testMoveShareGroupNotRecipient(): void {
4575
+        $this->expectException(\InvalidArgumentException::class);
4576
+        $this->expectExceptionMessage('Invalid share recipient');
4577 4577
 
4578
-		$share = $this->manager->newShare();
4579
-		$share->setShareType(IShare::TYPE_GROUP);
4578
+        $share = $this->manager->newShare();
4579
+        $share->setShareType(IShare::TYPE_GROUP);
4580 4580
 
4581
-		$sharedWith = $this->createMock(IGroup::class);
4582
-		$share->setSharedWith('shareWith');
4581
+        $sharedWith = $this->createMock(IGroup::class);
4582
+        $share->setSharedWith('shareWith');
4583 4583
 
4584
-		$recipient = $this->createMock(IUser::class);
4585
-		$sharedWith->method('inGroup')->with($recipient)->willReturn(false);
4584
+        $recipient = $this->createMock(IUser::class);
4585
+        $sharedWith->method('inGroup')->with($recipient)->willReturn(false);
4586 4586
 
4587
-		$this->groupManager->method('get')->with('shareWith')->willReturn($sharedWith);
4588
-		$this->userManager->method('get')->with('recipient')->willReturn($recipient);
4589
-
4590
-		$this->manager->moveShare($share, 'recipient');
4591
-	}
4587
+        $this->groupManager->method('get')->with('shareWith')->willReturn($sharedWith);
4588
+        $this->userManager->method('get')->with('recipient')->willReturn($recipient);
4589
+
4590
+        $this->manager->moveShare($share, 'recipient');
4591
+    }
4592 4592
 
4593 4593
 
4594
-	public function testMoveShareGroupNull(): void {
4595
-		$this->expectException(\InvalidArgumentException::class);
4596
-		$this->expectExceptionMessage('Group "shareWith" does not exist');
4594
+    public function testMoveShareGroupNull(): void {
4595
+        $this->expectException(\InvalidArgumentException::class);
4596
+        $this->expectExceptionMessage('Group "shareWith" does not exist');
4597 4597
 
4598
-		$share = $this->manager->newShare();
4599
-		$share->setShareType(IShare::TYPE_GROUP);
4600
-		$share->setSharedWith('shareWith');
4598
+        $share = $this->manager->newShare();
4599
+        $share->setShareType(IShare::TYPE_GROUP);
4600
+        $share->setSharedWith('shareWith');
4601 4601
 
4602
-		$recipient = $this->createMock(IUser::class);
4602
+        $recipient = $this->createMock(IUser::class);
4603 4603
 
4604
-		$this->groupManager->method('get')->with('shareWith')->willReturn(null);
4605
-		$this->userManager->method('get')->with('recipient')->willReturn($recipient);
4604
+        $this->groupManager->method('get')->with('shareWith')->willReturn(null);
4605
+        $this->userManager->method('get')->with('recipient')->willReturn($recipient);
4606 4606
 
4607
-		$this->manager->moveShare($share, 'recipient');
4608
-	}
4607
+        $this->manager->moveShare($share, 'recipient');
4608
+    }
4609 4609
 
4610
-	public function testMoveShareGroup(): void {
4611
-		$share = $this->manager->newShare();
4612
-		$share->setShareType(IShare::TYPE_GROUP)
4613
-			->setId('42')
4614
-			->setProviderId('foo');
4610
+    public function testMoveShareGroup(): void {
4611
+        $share = $this->manager->newShare();
4612
+        $share->setShareType(IShare::TYPE_GROUP)
4613
+            ->setId('42')
4614
+            ->setProviderId('foo');
4615 4615
 
4616
-		$group = $this->createMock(IGroup::class);
4617
-		$share->setSharedWith('group');
4616
+        $group = $this->createMock(IGroup::class);
4617
+        $share->setSharedWith('group');
4618 4618
 
4619
-		$recipient = $this->createMock(IUser::class);
4620
-		$group->method('inGroup')->with($recipient)->willReturn(true);
4619
+        $recipient = $this->createMock(IUser::class);
4620
+        $group->method('inGroup')->with($recipient)->willReturn(true);
4621 4621
 
4622
-		$this->groupManager->method('get')->with('group')->willReturn($group);
4623
-		$this->userManager->method('get')->with('recipient')->willReturn($recipient);
4622
+        $this->groupManager->method('get')->with('group')->willReturn($group);
4623
+        $this->userManager->method('get')->with('recipient')->willReturn($recipient);
4624 4624
 
4625
-		$this->defaultProvider->method('move')->with($share, 'recipient')->willReturnArgument(0);
4625
+        $this->defaultProvider->method('move')->with($share, 'recipient')->willReturnArgument(0);
4626 4626
 
4627
-		$this->manager->moveShare($share, 'recipient');
4628
-		$this->addToAssertionCount(1);
4629
-	}
4627
+        $this->manager->moveShare($share, 'recipient');
4628
+        $this->addToAssertionCount(1);
4629
+    }
4630 4630
 
4631
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataTestShareProviderExists')]
4632
-	public function testShareProviderExists($shareType, $expected): void {
4633
-		$factory = $this->getMockBuilder('OCP\Share\IProviderFactory')->getMock();
4634
-		$factory->expects($this->any())->method('getProviderForType')
4635
-			->willReturnCallback(function ($id) {
4636
-				if ($id === IShare::TYPE_USER) {
4637
-					return true;
4638
-				}
4639
-				throw new ProviderException();
4640
-			});
4631
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataTestShareProviderExists')]
4632
+    public function testShareProviderExists($shareType, $expected): void {
4633
+        $factory = $this->getMockBuilder('OCP\Share\IProviderFactory')->getMock();
4634
+        $factory->expects($this->any())->method('getProviderForType')
4635
+            ->willReturnCallback(function ($id) {
4636
+                if ($id === IShare::TYPE_USER) {
4637
+                    return true;
4638
+                }
4639
+                throw new ProviderException();
4640
+            });
4641 4641
 
4642
-		$manager = $this->createManager($factory);
4643
-		$this->assertSame($expected,
4644
-			$manager->shareProviderExists($shareType)
4645
-		);
4646
-	}
4642
+        $manager = $this->createManager($factory);
4643
+        $this->assertSame($expected,
4644
+            $manager->shareProviderExists($shareType)
4645
+        );
4646
+    }
4647 4647
 
4648
-	public static function dataTestShareProviderExists() {
4649
-		return [
4650
-			[IShare::TYPE_USER, true],
4651
-			[42, false],
4652
-		];
4653
-	}
4648
+    public static function dataTestShareProviderExists() {
4649
+        return [
4650
+            [IShare::TYPE_USER, true],
4651
+            [42, false],
4652
+        ];
4653
+    }
4654 4654
 
4655
-	public function testGetSharesInFolder(): void {
4656
-		$factory = new DummyFactory2($this->createMock(IServerContainer::class));
4655
+    public function testGetSharesInFolder(): void {
4656
+        $factory = new DummyFactory2($this->createMock(IServerContainer::class));
4657 4657
 
4658
-		$manager = $this->createManager($factory);
4658
+        $manager = $this->createManager($factory);
4659 4659
 
4660
-		$factory->setProvider($this->defaultProvider);
4661
-		$extraProvider = $this->createMock(IShareProvider::class);
4662
-		$factory->setSecondProvider($extraProvider);
4660
+        $factory->setProvider($this->defaultProvider);
4661
+        $extraProvider = $this->createMock(IShareProvider::class);
4662
+        $factory->setSecondProvider($extraProvider);
4663 4663
 
4664
-		$share1 = $this->createMock(IShare::class);
4665
-		$share2 = $this->createMock(IShare::class);
4666
-		$share3 = $this->createMock(IShare::class);
4667
-		$share4 = $this->createMock(IShare::class);
4664
+        $share1 = $this->createMock(IShare::class);
4665
+        $share2 = $this->createMock(IShare::class);
4666
+        $share3 = $this->createMock(IShare::class);
4667
+        $share4 = $this->createMock(IShare::class);
4668 4668
 
4669
-		$folder = $this->createMock(Folder::class);
4669
+        $folder = $this->createMock(Folder::class);
4670 4670
 
4671
-		$this->defaultProvider->method('getSharesInFolder')
4672
-			->with(
4673
-				$this->equalTo('user'),
4674
-				$this->equalTo($folder),
4675
-				$this->equalTo(false)
4676
-			)->willReturn([
4677
-				1 => [$share1],
4678
-				2 => [$share2],
4679
-			]);
4680
-
4681
-		$extraProvider->method('getSharesInFolder')
4682
-			->with(
4683
-				$this->equalTo('user'),
4684
-				$this->equalTo($folder),
4685
-				$this->equalTo(false)
4686
-			)->willReturn([
4687
-				2 => [$share3],
4688
-				3 => [$share4],
4689
-			]);
4690
-
4691
-		$result = $manager->getSharesInFolder('user', $folder, false);
4692
-
4693
-		$expects = [
4694
-			1 => [$share1],
4695
-			2 => [$share2, $share3],
4696
-			3 => [$share4],
4697
-		];
4698
-
4699
-		$this->assertSame($expects, $result);
4700
-	}
4701
-
4702
-	public function testGetSharesInFolderOwnerless(): void {
4703
-		$factory = new DummyFactory2($this->createMock(IServerContainer::class));
4704
-
4705
-		$manager = $this->createManager($factory);
4706
-
4707
-		$factory->setProvider($this->defaultProvider);
4708
-		$extraProvider = $this->createMock(IShareProviderSupportsAllSharesInFolder::class);
4709
-		$factory->setSecondProvider($extraProvider);
4710
-
4711
-		$share1 = $this->createMock(IShare::class);
4712
-		$share2 = $this->createMock(IShare::class);
4713
-
4714
-		$mount = $this->createMock(IShareOwnerlessMount::class);
4715
-
4716
-		$folder = $this->createMock(Folder::class);
4717
-		$folder
4718
-			->method('getMountPoint')
4719
-			->willReturn($mount);
4720
-
4721
-		$this->defaultProvider
4722
-			->method('getAllSharesInFolder')
4723
-			->with($folder)
4724
-			->willReturn([1 => [$share1]]);
4725
-
4726
-		$extraProvider
4727
-			->method('getAllSharesInFolder')
4728
-			->with($folder)
4729
-			->willReturn([1 => [$share2]]);
4730
-
4731
-		$this->assertSame([
4732
-			1 => [$share1, $share2],
4733
-		], $manager->getSharesInFolder('user', $folder));
4734
-	}
4735
-
4736
-
4737
-	public function testGetAccessList(): void {
4738
-		$factory = new DummyFactory2($this->createMock(IServerContainer::class));
4739
-
4740
-		$manager = $this->createManager($factory);
4741
-
4742
-		$factory->setProvider($this->defaultProvider);
4743
-		$extraProvider = $this->createMock(IShareProvider::class);
4744
-		$factory->setSecondProvider($extraProvider);
4745
-
4746
-		$nodeOwner = $this->createMock(IUser::class);
4747
-		$nodeOwner->expects($this->once())
4748
-			->method('getUID')
4749
-			->willReturn('user1');
4750
-
4751
-		$node = $this->createMock(Node::class);
4752
-		$node->expects($this->once())
4753
-			->method('getOwner')
4754
-			->willReturn($nodeOwner);
4755
-		$node->method('getId')
4756
-			->willReturn(42);
4757
-
4758
-		$userFolder = $this->createMock(Folder::class);
4759
-		$file = $this->createMock(File::class);
4760
-		$folder = $this->createMock(Folder::class);
4761
-
4762
-		$owner = $this->createMock(IUser::class);
4763
-		$owner->expects($this->once())
4764
-			->method('getUID')
4765
-			->willReturn('owner');
4766
-
4767
-		$file->method('getParent')
4768
-			->willReturn($folder);
4769
-		$file->method('getPath')
4770
-			->willReturn('/owner/files/folder/file');
4771
-		$file->method('getOwner')
4772
-			->willReturn($owner);
4773
-		$file->method('getId')
4774
-			->willReturn(23);
4775
-		$folder->method('getParent')
4776
-			->willReturn($userFolder);
4777
-		$folder->method('getPath')
4778
-			->willReturn('/owner/files/folder');
4779
-		$userFolder->method('getFirstNodeById')
4780
-			->with($this->equalTo(42))
4781
-			->willReturn($file);
4782
-		$userFolder->method('getPath')
4783
-			->willReturn('/user1/files');
4784
-
4785
-		$this->userManager->method('userExists')
4786
-			->with($this->equalTo('user1'))
4787
-			->willReturn(true);
4788
-
4789
-		$this->defaultProvider->method('getAccessList')
4790
-			->with(
4791
-				$this->equalTo([$file, $folder]),
4792
-				false
4793
-			)
4794
-			->willReturn([
4795
-				'users' => [
4796
-					'user1',
4797
-					'user2',
4798
-					'user3',
4799
-					'123456',
4800
-				],
4801
-				'public' => true,
4802
-			]);
4803
-
4804
-		$extraProvider->method('getAccessList')
4805
-			->with(
4806
-				$this->equalTo([$file, $folder]),
4807
-				false
4808
-			)
4809
-			->willReturn([
4810
-				'users' => [
4811
-					'user3',
4812
-					'user4',
4813
-					'user5',
4814
-					'234567',
4815
-				],
4816
-				'remote' => true,
4817
-			]);
4818
-
4819
-		$this->rootFolder->method('getUserFolder')
4820
-			->with($this->equalTo('user1'))
4821
-			->willReturn($userFolder);
4822
-
4823
-		$expected = [
4824
-			'users' => ['owner', 'user1', 'user2', 'user3', '123456','user4', 'user5', '234567'],
4825
-			'remote' => true,
4826
-			'public' => true,
4827
-		];
4828
-
4829
-		$result = $manager->getAccessList($node, true, false);
4830
-
4831
-		$this->assertSame($expected['public'], $result['public']);
4832
-		$this->assertSame($expected['remote'], $result['remote']);
4833
-		$this->assertSame($expected['users'], $result['users']);
4834
-	}
4835
-
4836
-	public function testGetAccessListWithCurrentAccess(): void {
4837
-		$factory = new DummyFactory2($this->createMock(IServerContainer::class));
4838
-
4839
-		$manager = $this->createManager($factory);
4840
-
4841
-		$factory->setProvider($this->defaultProvider);
4842
-		$extraProvider = $this->createMock(IShareProvider::class);
4843
-		$factory->setSecondProvider($extraProvider);
4844
-
4845
-		$nodeOwner = $this->createMock(IUser::class);
4846
-		$nodeOwner->expects($this->once())
4847
-			->method('getUID')
4848
-			->willReturn('user1');
4849
-
4850
-		$node = $this->createMock(Node::class);
4851
-		$node->expects($this->once())
4852
-			->method('getOwner')
4853
-			->willReturn($nodeOwner);
4854
-		$node->method('getId')
4855
-			->willReturn(42);
4856
-
4857
-		$userFolder = $this->createMock(Folder::class);
4858
-		$file = $this->createMock(File::class);
4859
-
4860
-		$owner = $this->createMock(IUser::class);
4861
-		$owner->expects($this->once())
4862
-			->method('getUID')
4863
-			->willReturn('owner');
4864
-		$folder = $this->createMock(Folder::class);
4865
-
4866
-		$file->method('getParent')
4867
-			->willReturn($folder);
4868
-		$file->method('getPath')
4869
-			->willReturn('/owner/files/folder/file');
4870
-		$file->method('getOwner')
4871
-			->willReturn($owner);
4872
-		$file->method('getId')
4873
-			->willReturn(23);
4874
-		$folder->method('getParent')
4875
-			->willReturn($userFolder);
4876
-		$folder->method('getPath')
4877
-			->willReturn('/owner/files/folder');
4878
-		$userFolder->method('getFirstNodeById')
4879
-			->with($this->equalTo(42))
4880
-			->willReturn($file);
4881
-		$userFolder->method('getPath')
4882
-			->willReturn('/user1/files');
4883
-
4884
-		$this->userManager->method('userExists')
4885
-			->with($this->equalTo('user1'))
4886
-			->willReturn(true);
4887
-
4888
-		$this->defaultProvider->method('getAccessList')
4889
-			->with(
4890
-				$this->equalTo([$file, $folder]),
4891
-				true
4892
-			)
4893
-			->willReturn([
4894
-				'users' => [
4895
-					'user1' => [],
4896
-					'user2' => [],
4897
-					'user3' => [],
4898
-					'123456' => [],
4899
-				],
4900
-				'public' => true,
4901
-			]);
4902
-
4903
-		$extraProvider->method('getAccessList')
4904
-			->with(
4905
-				$this->equalTo([$file, $folder]),
4906
-				true
4907
-			)
4908
-			->willReturn([
4909
-				'users' => [
4910
-					'user3' => [],
4911
-					'user4' => [],
4912
-					'user5' => [],
4913
-					'234567' => [],
4914
-				],
4915
-				'remote' => [
4916
-					'remote1',
4917
-				],
4918
-			]);
4919
-
4920
-		$this->rootFolder->method('getUserFolder')
4921
-			->with($this->equalTo('user1'))
4922
-			->willReturn($userFolder);
4923
-
4924
-		$expected = [
4925
-			'users' => [
4926
-				'owner' => [
4927
-					'node_id' => 23,
4928
-					'node_path' => '/folder/file'
4929
-				]
4930
-				, 'user1' => [], 'user2' => [], 'user3' => [], '123456' => [], 'user4' => [], 'user5' => [], '234567' => []],
4931
-			'remote' => [
4932
-				'remote1',
4933
-			],
4934
-			'public' => true,
4935
-		];
4936
-
4937
-		$result = $manager->getAccessList($node, true, true);
4938
-
4939
-		$this->assertSame($expected['public'], $result['public']);
4940
-		$this->assertSame($expected['remote'], $result['remote']);
4941
-		$this->assertSame($expected['users'], $result['users']);
4942
-	}
4943
-
4944
-	public function testGetAllShares(): void {
4945
-		$factory = new DummyFactory2($this->createMock(IServerContainer::class));
4946
-
4947
-		$manager = $this->createManager($factory);
4948
-
4949
-		$factory->setProvider($this->defaultProvider);
4950
-		$extraProvider = $this->createMock(IShareProvider::class);
4951
-		$factory->setSecondProvider($extraProvider);
4952
-
4953
-		$share1 = $this->createMock(IShare::class);
4954
-		$share2 = $this->createMock(IShare::class);
4955
-		$share3 = $this->createMock(IShare::class);
4956
-		$share4 = $this->createMock(IShare::class);
4957
-
4958
-		$this->defaultProvider->method('getAllShares')
4959
-			->willReturnCallback(function () use ($share1, $share2) {
4960
-				yield $share1;
4961
-				yield $share2;
4962
-			});
4963
-		$extraProvider->method('getAllShares')
4964
-			->willReturnCallback(function () use ($share3, $share4) {
4965
-				yield $share3;
4966
-				yield $share4;
4967
-			});
4968
-
4969
-		// "yield from", used in "getAllShares()", does not reset the keys, so
4970
-		// "use_keys" has to be disabled to collect all the values while
4971
-		// ignoring the keys returned by the generator.
4972
-		$result = iterator_to_array($manager->getAllShares(), $use_keys = false);
4973
-
4974
-		$expects = [$share1, $share2, $share3, $share4];
4975
-
4976
-		$this->assertSame($expects, $result);
4977
-	}
4978
-
4979
-	public static function dataCurrentUserCanEnumerateTargetUser(): array {
4980
-		return [
4981
-			'Full match guest' => [true, true, false, false, false, false, false, true],
4982
-			'Full match user' => [false, true, false, false, false, false, false, true],
4983
-			'Enumeration off guest' => [true, false, false, false, false, false, false, false],
4984
-			'Enumeration off user' => [false, false, false, false, false, false, false, false],
4985
-			'Enumeration guest' => [true, false, true, false, false, false, false, true],
4986
-			'Enumeration user' => [false, false, true, false, false, false, false, true],
4987
-
4988
-			// Restricted enumerations guests never works
4989
-			'Guest phone' => [true, false, true, true, false, false, false, false],
4990
-			'Guest group' => [true, false, true, false, true, false, false, false],
4991
-			'Guest both' => [true, false, true, true, true, false, false, false],
4992
-
4993
-			// Restricted enumerations users
4994
-			'User phone but not known' => [false, false, true, true, false, false, false, false],
4995
-			'User phone known' => [false, false, true, true, false, true, false, true],
4996
-			'User group but no match' => [false, false, true, false, true, false, false, false],
4997
-			'User group with match' => [false, false, true, false, true, false, true, true],
4998
-		];
4999
-	}
5000
-
5001
-	/**
5002
-	 * @param bool $expected
5003
-	 */
5004
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataCurrentUserCanEnumerateTargetUser')]
5005
-	public function testCurrentUserCanEnumerateTargetUser(bool $currentUserIsGuest, bool $allowEnumerationFullMatch, bool $allowEnumeration, bool $limitEnumerationToPhone, bool $limitEnumerationToGroups, bool $isKnownToUser, bool $haveCommonGroup, bool $expected): void {
5006
-		/** @var IManager|MockObject $manager */
5007
-		$manager = $this->createManagerMock()
5008
-			->onlyMethods([
5009
-				'allowEnumerationFullMatch',
5010
-				'allowEnumeration',
5011
-				'limitEnumerationToPhone',
5012
-				'limitEnumerationToGroups',
5013
-			])
5014
-			->getMock();
5015
-
5016
-		$manager->method('allowEnumerationFullMatch')
5017
-			->willReturn($allowEnumerationFullMatch);
5018
-		$manager->method('allowEnumeration')
5019
-			->willReturn($allowEnumeration);
5020
-		$manager->method('limitEnumerationToPhone')
5021
-			->willReturn($limitEnumerationToPhone);
5022
-		$manager->method('limitEnumerationToGroups')
5023
-			->willReturn($limitEnumerationToGroups);
5024
-
5025
-		$this->knownUserService->method('isKnownToUser')
5026
-			->with('current', 'target')
5027
-			->willReturn($isKnownToUser);
5028
-
5029
-		$currentUser = null;
5030
-		if (!$currentUserIsGuest) {
5031
-			$currentUser = $this->createMock(IUser::class);
5032
-			$currentUser->method('getUID')
5033
-				->willReturn('current');
5034
-		}
5035
-		$targetUser = $this->createMock(IUser::class);
5036
-		$targetUser->method('getUID')
5037
-			->willReturn('target');
5038
-
5039
-		if ($haveCommonGroup) {
5040
-			$this->groupManager->method('getUserGroupIds')
5041
-				->willReturnMap([
5042
-					[$targetUser, ['gid1', 'gid2']],
5043
-					[$currentUser, ['gid2', 'gid3']],
5044
-				]);
5045
-		} else {
5046
-			$this->groupManager->method('getUserGroupIds')
5047
-				->willReturnMap([
5048
-					[$targetUser, ['gid1', 'gid2']],
5049
-					[$currentUser, ['gid3', 'gid4']],
5050
-				]);
5051
-		}
5052
-
5053
-		$this->assertSame($expected, $manager->currentUserCanEnumerateTargetUser($currentUser, $targetUser));
5054
-	}
4671
+        $this->defaultProvider->method('getSharesInFolder')
4672
+            ->with(
4673
+                $this->equalTo('user'),
4674
+                $this->equalTo($folder),
4675
+                $this->equalTo(false)
4676
+            )->willReturn([
4677
+                1 => [$share1],
4678
+                2 => [$share2],
4679
+            ]);
4680
+
4681
+        $extraProvider->method('getSharesInFolder')
4682
+            ->with(
4683
+                $this->equalTo('user'),
4684
+                $this->equalTo($folder),
4685
+                $this->equalTo(false)
4686
+            )->willReturn([
4687
+                2 => [$share3],
4688
+                3 => [$share4],
4689
+            ]);
4690
+
4691
+        $result = $manager->getSharesInFolder('user', $folder, false);
4692
+
4693
+        $expects = [
4694
+            1 => [$share1],
4695
+            2 => [$share2, $share3],
4696
+            3 => [$share4],
4697
+        ];
4698
+
4699
+        $this->assertSame($expects, $result);
4700
+    }
4701
+
4702
+    public function testGetSharesInFolderOwnerless(): void {
4703
+        $factory = new DummyFactory2($this->createMock(IServerContainer::class));
4704
+
4705
+        $manager = $this->createManager($factory);
4706
+
4707
+        $factory->setProvider($this->defaultProvider);
4708
+        $extraProvider = $this->createMock(IShareProviderSupportsAllSharesInFolder::class);
4709
+        $factory->setSecondProvider($extraProvider);
4710
+
4711
+        $share1 = $this->createMock(IShare::class);
4712
+        $share2 = $this->createMock(IShare::class);
4713
+
4714
+        $mount = $this->createMock(IShareOwnerlessMount::class);
4715
+
4716
+        $folder = $this->createMock(Folder::class);
4717
+        $folder
4718
+            ->method('getMountPoint')
4719
+            ->willReturn($mount);
4720
+
4721
+        $this->defaultProvider
4722
+            ->method('getAllSharesInFolder')
4723
+            ->with($folder)
4724
+            ->willReturn([1 => [$share1]]);
4725
+
4726
+        $extraProvider
4727
+            ->method('getAllSharesInFolder')
4728
+            ->with($folder)
4729
+            ->willReturn([1 => [$share2]]);
4730
+
4731
+        $this->assertSame([
4732
+            1 => [$share1, $share2],
4733
+        ], $manager->getSharesInFolder('user', $folder));
4734
+    }
4735
+
4736
+
4737
+    public function testGetAccessList(): void {
4738
+        $factory = new DummyFactory2($this->createMock(IServerContainer::class));
4739
+
4740
+        $manager = $this->createManager($factory);
4741
+
4742
+        $factory->setProvider($this->defaultProvider);
4743
+        $extraProvider = $this->createMock(IShareProvider::class);
4744
+        $factory->setSecondProvider($extraProvider);
4745
+
4746
+        $nodeOwner = $this->createMock(IUser::class);
4747
+        $nodeOwner->expects($this->once())
4748
+            ->method('getUID')
4749
+            ->willReturn('user1');
4750
+
4751
+        $node = $this->createMock(Node::class);
4752
+        $node->expects($this->once())
4753
+            ->method('getOwner')
4754
+            ->willReturn($nodeOwner);
4755
+        $node->method('getId')
4756
+            ->willReturn(42);
4757
+
4758
+        $userFolder = $this->createMock(Folder::class);
4759
+        $file = $this->createMock(File::class);
4760
+        $folder = $this->createMock(Folder::class);
4761
+
4762
+        $owner = $this->createMock(IUser::class);
4763
+        $owner->expects($this->once())
4764
+            ->method('getUID')
4765
+            ->willReturn('owner');
4766
+
4767
+        $file->method('getParent')
4768
+            ->willReturn($folder);
4769
+        $file->method('getPath')
4770
+            ->willReturn('/owner/files/folder/file');
4771
+        $file->method('getOwner')
4772
+            ->willReturn($owner);
4773
+        $file->method('getId')
4774
+            ->willReturn(23);
4775
+        $folder->method('getParent')
4776
+            ->willReturn($userFolder);
4777
+        $folder->method('getPath')
4778
+            ->willReturn('/owner/files/folder');
4779
+        $userFolder->method('getFirstNodeById')
4780
+            ->with($this->equalTo(42))
4781
+            ->willReturn($file);
4782
+        $userFolder->method('getPath')
4783
+            ->willReturn('/user1/files');
4784
+
4785
+        $this->userManager->method('userExists')
4786
+            ->with($this->equalTo('user1'))
4787
+            ->willReturn(true);
4788
+
4789
+        $this->defaultProvider->method('getAccessList')
4790
+            ->with(
4791
+                $this->equalTo([$file, $folder]),
4792
+                false
4793
+            )
4794
+            ->willReturn([
4795
+                'users' => [
4796
+                    'user1',
4797
+                    'user2',
4798
+                    'user3',
4799
+                    '123456',
4800
+                ],
4801
+                'public' => true,
4802
+            ]);
4803
+
4804
+        $extraProvider->method('getAccessList')
4805
+            ->with(
4806
+                $this->equalTo([$file, $folder]),
4807
+                false
4808
+            )
4809
+            ->willReturn([
4810
+                'users' => [
4811
+                    'user3',
4812
+                    'user4',
4813
+                    'user5',
4814
+                    '234567',
4815
+                ],
4816
+                'remote' => true,
4817
+            ]);
4818
+
4819
+        $this->rootFolder->method('getUserFolder')
4820
+            ->with($this->equalTo('user1'))
4821
+            ->willReturn($userFolder);
4822
+
4823
+        $expected = [
4824
+            'users' => ['owner', 'user1', 'user2', 'user3', '123456','user4', 'user5', '234567'],
4825
+            'remote' => true,
4826
+            'public' => true,
4827
+        ];
4828
+
4829
+        $result = $manager->getAccessList($node, true, false);
4830
+
4831
+        $this->assertSame($expected['public'], $result['public']);
4832
+        $this->assertSame($expected['remote'], $result['remote']);
4833
+        $this->assertSame($expected['users'], $result['users']);
4834
+    }
4835
+
4836
+    public function testGetAccessListWithCurrentAccess(): void {
4837
+        $factory = new DummyFactory2($this->createMock(IServerContainer::class));
4838
+
4839
+        $manager = $this->createManager($factory);
4840
+
4841
+        $factory->setProvider($this->defaultProvider);
4842
+        $extraProvider = $this->createMock(IShareProvider::class);
4843
+        $factory->setSecondProvider($extraProvider);
4844
+
4845
+        $nodeOwner = $this->createMock(IUser::class);
4846
+        $nodeOwner->expects($this->once())
4847
+            ->method('getUID')
4848
+            ->willReturn('user1');
4849
+
4850
+        $node = $this->createMock(Node::class);
4851
+        $node->expects($this->once())
4852
+            ->method('getOwner')
4853
+            ->willReturn($nodeOwner);
4854
+        $node->method('getId')
4855
+            ->willReturn(42);
4856
+
4857
+        $userFolder = $this->createMock(Folder::class);
4858
+        $file = $this->createMock(File::class);
4859
+
4860
+        $owner = $this->createMock(IUser::class);
4861
+        $owner->expects($this->once())
4862
+            ->method('getUID')
4863
+            ->willReturn('owner');
4864
+        $folder = $this->createMock(Folder::class);
4865
+
4866
+        $file->method('getParent')
4867
+            ->willReturn($folder);
4868
+        $file->method('getPath')
4869
+            ->willReturn('/owner/files/folder/file');
4870
+        $file->method('getOwner')
4871
+            ->willReturn($owner);
4872
+        $file->method('getId')
4873
+            ->willReturn(23);
4874
+        $folder->method('getParent')
4875
+            ->willReturn($userFolder);
4876
+        $folder->method('getPath')
4877
+            ->willReturn('/owner/files/folder');
4878
+        $userFolder->method('getFirstNodeById')
4879
+            ->with($this->equalTo(42))
4880
+            ->willReturn($file);
4881
+        $userFolder->method('getPath')
4882
+            ->willReturn('/user1/files');
4883
+
4884
+        $this->userManager->method('userExists')
4885
+            ->with($this->equalTo('user1'))
4886
+            ->willReturn(true);
4887
+
4888
+        $this->defaultProvider->method('getAccessList')
4889
+            ->with(
4890
+                $this->equalTo([$file, $folder]),
4891
+                true
4892
+            )
4893
+            ->willReturn([
4894
+                'users' => [
4895
+                    'user1' => [],
4896
+                    'user2' => [],
4897
+                    'user3' => [],
4898
+                    '123456' => [],
4899
+                ],
4900
+                'public' => true,
4901
+            ]);
4902
+
4903
+        $extraProvider->method('getAccessList')
4904
+            ->with(
4905
+                $this->equalTo([$file, $folder]),
4906
+                true
4907
+            )
4908
+            ->willReturn([
4909
+                'users' => [
4910
+                    'user3' => [],
4911
+                    'user4' => [],
4912
+                    'user5' => [],
4913
+                    '234567' => [],
4914
+                ],
4915
+                'remote' => [
4916
+                    'remote1',
4917
+                ],
4918
+            ]);
4919
+
4920
+        $this->rootFolder->method('getUserFolder')
4921
+            ->with($this->equalTo('user1'))
4922
+            ->willReturn($userFolder);
4923
+
4924
+        $expected = [
4925
+            'users' => [
4926
+                'owner' => [
4927
+                    'node_id' => 23,
4928
+                    'node_path' => '/folder/file'
4929
+                ]
4930
+                , 'user1' => [], 'user2' => [], 'user3' => [], '123456' => [], 'user4' => [], 'user5' => [], '234567' => []],
4931
+            'remote' => [
4932
+                'remote1',
4933
+            ],
4934
+            'public' => true,
4935
+        ];
4936
+
4937
+        $result = $manager->getAccessList($node, true, true);
4938
+
4939
+        $this->assertSame($expected['public'], $result['public']);
4940
+        $this->assertSame($expected['remote'], $result['remote']);
4941
+        $this->assertSame($expected['users'], $result['users']);
4942
+    }
4943
+
4944
+    public function testGetAllShares(): void {
4945
+        $factory = new DummyFactory2($this->createMock(IServerContainer::class));
4946
+
4947
+        $manager = $this->createManager($factory);
4948
+
4949
+        $factory->setProvider($this->defaultProvider);
4950
+        $extraProvider = $this->createMock(IShareProvider::class);
4951
+        $factory->setSecondProvider($extraProvider);
4952
+
4953
+        $share1 = $this->createMock(IShare::class);
4954
+        $share2 = $this->createMock(IShare::class);
4955
+        $share3 = $this->createMock(IShare::class);
4956
+        $share4 = $this->createMock(IShare::class);
4957
+
4958
+        $this->defaultProvider->method('getAllShares')
4959
+            ->willReturnCallback(function () use ($share1, $share2) {
4960
+                yield $share1;
4961
+                yield $share2;
4962
+            });
4963
+        $extraProvider->method('getAllShares')
4964
+            ->willReturnCallback(function () use ($share3, $share4) {
4965
+                yield $share3;
4966
+                yield $share4;
4967
+            });
4968
+
4969
+        // "yield from", used in "getAllShares()", does not reset the keys, so
4970
+        // "use_keys" has to be disabled to collect all the values while
4971
+        // ignoring the keys returned by the generator.
4972
+        $result = iterator_to_array($manager->getAllShares(), $use_keys = false);
4973
+
4974
+        $expects = [$share1, $share2, $share3, $share4];
4975
+
4976
+        $this->assertSame($expects, $result);
4977
+    }
4978
+
4979
+    public static function dataCurrentUserCanEnumerateTargetUser(): array {
4980
+        return [
4981
+            'Full match guest' => [true, true, false, false, false, false, false, true],
4982
+            'Full match user' => [false, true, false, false, false, false, false, true],
4983
+            'Enumeration off guest' => [true, false, false, false, false, false, false, false],
4984
+            'Enumeration off user' => [false, false, false, false, false, false, false, false],
4985
+            'Enumeration guest' => [true, false, true, false, false, false, false, true],
4986
+            'Enumeration user' => [false, false, true, false, false, false, false, true],
4987
+
4988
+            // Restricted enumerations guests never works
4989
+            'Guest phone' => [true, false, true, true, false, false, false, false],
4990
+            'Guest group' => [true, false, true, false, true, false, false, false],
4991
+            'Guest both' => [true, false, true, true, true, false, false, false],
4992
+
4993
+            // Restricted enumerations users
4994
+            'User phone but not known' => [false, false, true, true, false, false, false, false],
4995
+            'User phone known' => [false, false, true, true, false, true, false, true],
4996
+            'User group but no match' => [false, false, true, false, true, false, false, false],
4997
+            'User group with match' => [false, false, true, false, true, false, true, true],
4998
+        ];
4999
+    }
5000
+
5001
+    /**
5002
+     * @param bool $expected
5003
+     */
5004
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataCurrentUserCanEnumerateTargetUser')]
5005
+    public function testCurrentUserCanEnumerateTargetUser(bool $currentUserIsGuest, bool $allowEnumerationFullMatch, bool $allowEnumeration, bool $limitEnumerationToPhone, bool $limitEnumerationToGroups, bool $isKnownToUser, bool $haveCommonGroup, bool $expected): void {
5006
+        /** @var IManager|MockObject $manager */
5007
+        $manager = $this->createManagerMock()
5008
+            ->onlyMethods([
5009
+                'allowEnumerationFullMatch',
5010
+                'allowEnumeration',
5011
+                'limitEnumerationToPhone',
5012
+                'limitEnumerationToGroups',
5013
+            ])
5014
+            ->getMock();
5015
+
5016
+        $manager->method('allowEnumerationFullMatch')
5017
+            ->willReturn($allowEnumerationFullMatch);
5018
+        $manager->method('allowEnumeration')
5019
+            ->willReturn($allowEnumeration);
5020
+        $manager->method('limitEnumerationToPhone')
5021
+            ->willReturn($limitEnumerationToPhone);
5022
+        $manager->method('limitEnumerationToGroups')
5023
+            ->willReturn($limitEnumerationToGroups);
5024
+
5025
+        $this->knownUserService->method('isKnownToUser')
5026
+            ->with('current', 'target')
5027
+            ->willReturn($isKnownToUser);
5028
+
5029
+        $currentUser = null;
5030
+        if (!$currentUserIsGuest) {
5031
+            $currentUser = $this->createMock(IUser::class);
5032
+            $currentUser->method('getUID')
5033
+                ->willReturn('current');
5034
+        }
5035
+        $targetUser = $this->createMock(IUser::class);
5036
+        $targetUser->method('getUID')
5037
+            ->willReturn('target');
5038
+
5039
+        if ($haveCommonGroup) {
5040
+            $this->groupManager->method('getUserGroupIds')
5041
+                ->willReturnMap([
5042
+                    [$targetUser, ['gid1', 'gid2']],
5043
+                    [$currentUser, ['gid2', 'gid3']],
5044
+                ]);
5045
+        } else {
5046
+            $this->groupManager->method('getUserGroupIds')
5047
+                ->willReturnMap([
5048
+                    [$targetUser, ['gid1', 'gid2']],
5049
+                    [$currentUser, ['gid3', 'gid4']],
5050
+                ]);
5051
+        }
5052
+
5053
+        $this->assertSame($expected, $manager->currentUserCanEnumerateTargetUser($currentUser, $targetUser));
5054
+    }
5055 5055
 }
5056 5056
 
5057 5057
 class DummyFactory implements IProviderFactory {
5058
-	/** @var IShareProvider */
5059
-	protected $provider;
5060
-
5061
-	public function __construct(IServerContainer $serverContainer) {
5062
-	}
5063
-
5064
-	/**
5065
-	 * @param IShareProvider $provider
5066
-	 */
5067
-	public function setProvider($provider) {
5068
-		$this->provider = $provider;
5069
-	}
5070
-
5071
-	/**
5072
-	 * @param string $id
5073
-	 * @return IShareProvider
5074
-	 */
5075
-	public function getProvider($id) {
5076
-		return $this->provider;
5077
-	}
5078
-
5079
-	/**
5080
-	 * @param int $shareType
5081
-	 * @return IShareProvider
5082
-	 */
5083
-	public function getProviderForType($shareType) {
5084
-		return $this->provider;
5085
-	}
5086
-
5087
-	/**
5088
-	 * @return IShareProvider[]
5089
-	 */
5090
-	public function getAllProviders() {
5091
-		return [$this->provider];
5092
-	}
5093
-
5094
-	public function registerProvider(string $shareProvier): void {
5095
-	}
5058
+    /** @var IShareProvider */
5059
+    protected $provider;
5060
+
5061
+    public function __construct(IServerContainer $serverContainer) {
5062
+    }
5063
+
5064
+    /**
5065
+     * @param IShareProvider $provider
5066
+     */
5067
+    public function setProvider($provider) {
5068
+        $this->provider = $provider;
5069
+    }
5070
+
5071
+    /**
5072
+     * @param string $id
5073
+     * @return IShareProvider
5074
+     */
5075
+    public function getProvider($id) {
5076
+        return $this->provider;
5077
+    }
5078
+
5079
+    /**
5080
+     * @param int $shareType
5081
+     * @return IShareProvider
5082
+     */
5083
+    public function getProviderForType($shareType) {
5084
+        return $this->provider;
5085
+    }
5086
+
5087
+    /**
5088
+     * @return IShareProvider[]
5089
+     */
5090
+    public function getAllProviders() {
5091
+        return [$this->provider];
5092
+    }
5093
+
5094
+    public function registerProvider(string $shareProvier): void {
5095
+    }
5096 5096
 }
5097 5097
 
5098 5098
 class DummyFactory2 extends DummyFactory {
5099
-	/** @var IShareProvider */
5100
-	private $provider2;
5101
-
5102
-	/**
5103
-	 * @param IShareProvider $provider
5104
-	 */
5105
-	public function setSecondProvider($provider) {
5106
-		$this->provider2 = $provider;
5107
-	}
5108
-
5109
-	public function getAllProviders() {
5110
-		return [$this->provider, $this->provider2];
5111
-	}
5112
-
5113
-	public function registerProvider(string $shareProvier): void {
5114
-	}
5099
+    /** @var IShareProvider */
5100
+    private $provider2;
5101
+
5102
+    /**
5103
+     * @param IShareProvider $provider
5104
+     */
5105
+    public function setSecondProvider($provider) {
5106
+        $this->provider2 = $provider;
5107
+    }
5108
+
5109
+    public function getAllProviders() {
5110
+        return [$this->provider, $this->provider2];
5111
+    }
5112
+
5113
+    public function registerProvider(string $shareProvier): void {
5114
+    }
5115 5115
 }
Please login to merge, or discard this patch.
lib/private/Share20/Manager.php 1 patch
Indentation   +2017 added lines, -2017 removed lines patch added patch discarded remove patch
@@ -63,2038 +63,2038 @@
 block discarded – undo
63 63
  */
64 64
 class Manager implements IManager {
65 65
 
66
-	private ?IL10N $l;
67
-	private LegacyHooks $legacyHooks;
68
-
69
-	public function __construct(
70
-		private LoggerInterface $logger,
71
-		private IConfig $config,
72
-		private ISecureRandom $secureRandom,
73
-		private IHasher $hasher,
74
-		private IMountManager $mountManager,
75
-		private IGroupManager $groupManager,
76
-		private IFactory $l10nFactory,
77
-		private IProviderFactory $factory,
78
-		private IUserManager $userManager,
79
-		private IRootFolder $rootFolder,
80
-		private IMailer $mailer,
81
-		private IURLGenerator $urlGenerator,
82
-		private \OC_Defaults $defaults,
83
-		private IEventDispatcher $dispatcher,
84
-		private IUserSession $userSession,
85
-		private KnownUserService $knownUserService,
86
-		private ShareDisableChecker $shareDisableChecker,
87
-		private IDateTimeZone $dateTimeZone,
88
-		private IAppConfig $appConfig,
89
-	) {
90
-		$this->l = $this->l10nFactory->get('lib');
91
-		// The constructor of LegacyHooks registers the listeners of share events
92
-		// do not remove if those are not properly migrated
93
-		$this->legacyHooks = new LegacyHooks($this->dispatcher);
94
-	}
95
-
96
-	/**
97
-	 * Convert from a full share id to a tuple (providerId, shareId)
98
-	 *
99
-	 * @param string $id
100
-	 * @return string[]
101
-	 */
102
-	private function splitFullId($id) {
103
-		return explode(':', $id, 2);
104
-	}
105
-
106
-	/**
107
-	 * Verify if a password meets all requirements
108
-	 *
109
-	 * @param string $password
110
-	 * @throws HintException
111
-	 */
112
-	protected function verifyPassword($password) {
113
-		if ($password === null) {
114
-			// No password is set, check if this is allowed.
115
-			if ($this->shareApiLinkEnforcePassword()) {
116
-				throw new \InvalidArgumentException($this->l->t('Passwords are enforced for link and mail shares'));
117
-			}
118
-
119
-			return;
120
-		}
121
-
122
-		// Let others verify the password
123
-		try {
124
-			$event = new ValidatePasswordPolicyEvent($password, PasswordContext::SHARING);
125
-			$this->dispatcher->dispatchTyped($event);
126
-		} catch (HintException $e) {
127
-			/* Wrap in a 400 bad request error */
128
-			throw new HintException($e->getMessage(), $e->getHint(), 400, $e);
129
-		}
130
-	}
131
-
132
-	/**
133
-	 * Check for generic requirements before creating a share
134
-	 *
135
-	 * @param IShare $share
136
-	 * @throws \InvalidArgumentException
137
-	 * @throws GenericShareException
138
-	 *
139
-	 * @suppress PhanUndeclaredClassMethod
140
-	 */
141
-	protected function generalCreateChecks(IShare $share, bool $isUpdate = false) {
142
-		if ($share->getShareType() === IShare::TYPE_USER) {
143
-			// We expect a valid user as sharedWith for user shares
144
-			if (!$this->userManager->userExists($share->getSharedWith())) {
145
-				throw new \InvalidArgumentException($this->l->t('Share recipient is not a valid user'));
146
-			}
147
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
148
-			// We expect a valid group as sharedWith for group shares
149
-			if (!$this->groupManager->groupExists($share->getSharedWith())) {
150
-				throw new \InvalidArgumentException($this->l->t('Share recipient is not a valid group'));
151
-			}
152
-		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
153
-			// No check for TYPE_EMAIL here as we have a recipient for them
154
-			if ($share->getSharedWith() !== null) {
155
-				throw new \InvalidArgumentException($this->l->t('Share recipient should be empty'));
156
-			}
157
-		} elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
158
-			if ($share->getSharedWith() === null) {
159
-				throw new \InvalidArgumentException($this->l->t('Share recipient should not be empty'));
160
-			}
161
-		} elseif ($share->getShareType() === IShare::TYPE_REMOTE) {
162
-			if ($share->getSharedWith() === null) {
163
-				throw new \InvalidArgumentException($this->l->t('Share recipient should not be empty'));
164
-			}
165
-		} elseif ($share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
166
-			if ($share->getSharedWith() === null) {
167
-				throw new \InvalidArgumentException($this->l->t('Share recipient should not be empty'));
168
-			}
169
-		} elseif ($share->getShareType() === IShare::TYPE_CIRCLE) {
170
-			$circle = \OCA\Circles\Api\v1\Circles::detailsCircle($share->getSharedWith());
171
-			if ($circle === null) {
172
-				throw new \InvalidArgumentException($this->l->t('Share recipient is not a valid circle'));
173
-			}
174
-		} elseif ($share->getShareType() === IShare::TYPE_ROOM) {
175
-		} elseif ($share->getShareType() === IShare::TYPE_DECK) {
176
-		} elseif ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
177
-		} else {
178
-			// We cannot handle other types yet
179
-			throw new \InvalidArgumentException($this->l->t('Unknown share type'));
180
-		}
181
-
182
-		// Verify the initiator of the share is set
183
-		if ($share->getSharedBy() === null) {
184
-			throw new \InvalidArgumentException($this->l->t('Share initiator must be set'));
185
-		}
186
-
187
-		// Cannot share with yourself
188
-		if ($share->getShareType() === IShare::TYPE_USER
189
-			&& $share->getSharedWith() === $share->getSharedBy()) {
190
-			throw new \InvalidArgumentException($this->l->t('Cannot share with yourself'));
191
-		}
192
-
193
-		// The path should be set
194
-		if ($share->getNode() === null) {
195
-			throw new \InvalidArgumentException($this->l->t('Shared path must be set'));
196
-		}
197
-
198
-		// And it should be a file or a folder
199
-		if (!($share->getNode() instanceof \OCP\Files\File)
200
-			&& !($share->getNode() instanceof \OCP\Files\Folder)) {
201
-			throw new \InvalidArgumentException($this->l->t('Shared path must be either a file or a folder'));
202
-		}
203
-
204
-		// And you cannot share your rootfolder
205
-		if ($this->userManager->userExists($share->getSharedBy())) {
206
-			$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
207
-		} else {
208
-			$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
209
-		}
210
-		if ($userFolder->getId() === $share->getNode()->getId()) {
211
-			throw new \InvalidArgumentException($this->l->t('You cannot share your root folder'));
212
-		}
213
-
214
-		// Check if we actually have share permissions
215
-		if (!$share->getNode()->isShareable()) {
216
-			throw new GenericShareException($this->l->t('You are not allowed to share %s', [$share->getNode()->getName()]), code: 404);
217
-		}
218
-
219
-		// Permissions should be set
220
-		if ($share->getPermissions() === null) {
221
-			throw new \InvalidArgumentException($this->l->t('Valid permissions are required for sharing'));
222
-		}
223
-
224
-		// Permissions must be valid
225
-		if ($share->getPermissions() < 0 || $share->getPermissions() > \OCP\Constants::PERMISSION_ALL) {
226
-			throw new \InvalidArgumentException($this->l->t('Valid permissions are required for sharing'));
227
-		}
228
-
229
-		// Single file shares should never have delete or create permissions
230
-		if (($share->getNode() instanceof File)
231
-			&& (($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_DELETE)) !== 0)) {
232
-			throw new \InvalidArgumentException($this->l->t('File shares cannot have create or delete permissions'));
233
-		}
234
-
235
-		$permissions = 0;
236
-		$nodesForUser = $userFolder->getById($share->getNodeId());
237
-		foreach ($nodesForUser as $node) {
238
-			if ($node->getInternalPath() === '' && !$node->getMountPoint() instanceof MoveableMount) {
239
-				// for the root of non-movable mount, the permissions we see if limited by the mount itself,
240
-				// so we instead use the "raw" permissions from the storage
241
-				$permissions |= $node->getStorage()->getPermissions('');
242
-			} else {
243
-				$permissions |= $node->getPermissions();
244
-			}
245
-		}
246
-
247
-		// Check that we do not share with more permissions than we have
248
-		if ($share->getPermissions() & ~$permissions) {
249
-			$path = $userFolder->getRelativePath($share->getNode()->getPath());
250
-			throw new GenericShareException($this->l->t('Cannot increase permissions of %s', [$path]), code: 404);
251
-		}
252
-
253
-		// Check that read permissions are always set
254
-		// Link shares are allowed to have no read permissions to allow upload to hidden folders
255
-		$noReadPermissionRequired = $share->getShareType() === IShare::TYPE_LINK
256
-			|| $share->getShareType() === IShare::TYPE_EMAIL;
257
-		if (!$noReadPermissionRequired
258
-			&& ($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
259
-			throw new \InvalidArgumentException($this->l->t('Shares need at least read permissions'));
260
-		}
261
-
262
-		if ($share->getNode() instanceof \OCP\Files\File) {
263
-			if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
264
-				throw new GenericShareException($this->l->t('Files cannot be shared with delete permissions'));
265
-			}
266
-			if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
267
-				throw new GenericShareException($this->l->t('Files cannot be shared with create permissions'));
268
-			}
269
-		}
270
-	}
271
-
272
-	/**
273
-	 * Validate if the expiration date fits the system settings
274
-	 *
275
-	 * @param IShare $share The share to validate the expiration date of
276
-	 * @return IShare The modified share object
277
-	 * @throws GenericShareException
278
-	 * @throws \InvalidArgumentException
279
-	 * @throws \Exception
280
-	 */
281
-	protected function validateExpirationDateInternal(IShare $share) {
282
-		$isRemote = $share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP;
283
-
284
-		$expirationDate = $share->getExpirationDate();
285
-
286
-		if ($isRemote) {
287
-			$defaultExpireDate = $this->shareApiRemoteDefaultExpireDate();
288
-			$defaultExpireDays = $this->shareApiRemoteDefaultExpireDays();
289
-			$configProp = 'remote_defaultExpDays';
290
-			$isEnforced = $this->shareApiRemoteDefaultExpireDateEnforced();
291
-		} else {
292
-			$defaultExpireDate = $this->shareApiInternalDefaultExpireDate();
293
-			$defaultExpireDays = $this->shareApiInternalDefaultExpireDays();
294
-			$configProp = 'internal_defaultExpDays';
295
-			$isEnforced = $this->shareApiInternalDefaultExpireDateEnforced();
296
-		}
297
-
298
-		// If $expirationDate is falsy, noExpirationDate is true and expiration not enforced
299
-		// Then skip expiration date validation as null is accepted
300
-		if (!$share->getNoExpirationDate() || $isEnforced) {
301
-			if ($expirationDate !== null) {
302
-				$expirationDate->setTimezone($this->dateTimeZone->getTimeZone());
303
-				$expirationDate->setTime(0, 0, 0);
304
-
305
-				$date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
306
-				$date->setTime(0, 0, 0);
307
-				if ($date >= $expirationDate) {
308
-					throw new GenericShareException($this->l->t('Expiration date is in the past'), code: 404);
309
-				}
310
-			}
311
-
312
-			// If expiredate is empty set a default one if there is a default
313
-			$fullId = null;
314
-			try {
315
-				$fullId = $share->getFullId();
316
-			} catch (\UnexpectedValueException $e) {
317
-				// This is a new share
318
-			}
319
-
320
-			if ($fullId === null && $expirationDate === null && $defaultExpireDate) {
321
-				$expirationDate = new \DateTime('now', $this->dateTimeZone->getTimeZone());
322
-				$expirationDate->setTime(0, 0, 0);
323
-				$days = (int)$this->config->getAppValue('core', $configProp, (string)$defaultExpireDays);
324
-				if ($days > $defaultExpireDays) {
325
-					$days = $defaultExpireDays;
326
-				}
327
-				$expirationDate->add(new \DateInterval('P' . $days . 'D'));
328
-			}
329
-
330
-			// If we enforce the expiration date check that is does not exceed
331
-			if ($isEnforced) {
332
-				if (empty($expirationDate)) {
333
-					throw new \InvalidArgumentException($this->l->t('Expiration date is enforced'));
334
-				}
335
-
336
-				$date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
337
-				$date->setTime(0, 0, 0);
338
-				$date->add(new \DateInterval('P' . $defaultExpireDays . 'D'));
339
-				if ($date < $expirationDate) {
340
-					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);
341
-				}
342
-			}
343
-		}
344
-
345
-		$accepted = true;
346
-		$message = '';
347
-		\OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
348
-			'expirationDate' => &$expirationDate,
349
-			'accepted' => &$accepted,
350
-			'message' => &$message,
351
-			'passwordSet' => $share->getPassword() !== null,
352
-		]);
353
-
354
-		if (!$accepted) {
355
-			throw new \Exception($message);
356
-		}
357
-
358
-		$share->setExpirationDate($expirationDate);
359
-
360
-		return $share;
361
-	}
362
-
363
-	/**
364
-	 * Validate if the expiration date fits the system settings
365
-	 *
366
-	 * @param IShare $share The share to validate the expiration date of
367
-	 * @return IShare The modified share object
368
-	 * @throws GenericShareException
369
-	 * @throws \InvalidArgumentException
370
-	 * @throws \Exception
371
-	 */
372
-	protected function validateExpirationDateLink(IShare $share) {
373
-		$expirationDate = $share->getExpirationDate();
374
-		$isEnforced = $this->shareApiLinkDefaultExpireDateEnforced();
375
-
376
-		// If $expirationDate is falsy, noExpirationDate is true and expiration not enforced
377
-		// Then skip expiration date validation as null is accepted
378
-		if (!($share->getNoExpirationDate() && !$isEnforced)) {
379
-			if ($expirationDate !== null) {
380
-				$expirationDate->setTimezone($this->dateTimeZone->getTimeZone());
381
-				$expirationDate->setTime(0, 0, 0);
382
-
383
-				$date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
384
-				$date->setTime(0, 0, 0);
385
-				if ($date >= $expirationDate) {
386
-					throw new GenericShareException($this->l->t('Expiration date is in the past'), code: 404);
387
-				}
388
-			}
389
-
390
-			// If expiredate is empty set a default one if there is a default
391
-			$fullId = null;
392
-			try {
393
-				$fullId = $share->getFullId();
394
-			} catch (\UnexpectedValueException $e) {
395
-				// This is a new share
396
-			}
397
-
398
-			if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
399
-				$expirationDate = new \DateTime('now', $this->dateTimeZone->getTimeZone());
400
-				$expirationDate->setTime(0, 0, 0);
401
-
402
-				$days = (int)$this->config->getAppValue('core', 'link_defaultExpDays', (string)$this->shareApiLinkDefaultExpireDays());
403
-				if ($days > $this->shareApiLinkDefaultExpireDays()) {
404
-					$days = $this->shareApiLinkDefaultExpireDays();
405
-				}
406
-				$expirationDate->add(new \DateInterval('P' . $days . 'D'));
407
-			}
408
-
409
-			// If we enforce the expiration date check that is does not exceed
410
-			if ($isEnforced) {
411
-				if (empty($expirationDate)) {
412
-					throw new \InvalidArgumentException($this->l->t('Expiration date is enforced'));
413
-				}
414
-
415
-				$date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
416
-				$date->setTime(0, 0, 0);
417
-				$date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
418
-				if ($date < $expirationDate) {
419
-					throw new GenericShareException(
420
-						$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()),
421
-						code: 404,
422
-					);
423
-				}
424
-			}
425
-
426
-		}
427
-
428
-		$accepted = true;
429
-		$message = '';
430
-		\OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
431
-			'expirationDate' => &$expirationDate,
432
-			'accepted' => &$accepted,
433
-			'message' => &$message,
434
-			'passwordSet' => $share->getPassword() !== null,
435
-		]);
436
-
437
-		if (!$accepted) {
438
-			throw new \Exception($message);
439
-		}
440
-
441
-		$share->setExpirationDate($expirationDate);
442
-
443
-		return $share;
444
-	}
445
-
446
-	/**
447
-	 * Check for pre share requirements for user shares
448
-	 *
449
-	 * @param IShare $share
450
-	 * @throws \Exception
451
-	 */
452
-	protected function userCreateChecks(IShare $share) {
453
-		// Check if we can share with group members only
454
-		if ($this->shareWithGroupMembersOnly()) {
455
-			$sharedBy = $this->userManager->get($share->getSharedBy());
456
-			$sharedWith = $this->userManager->get($share->getSharedWith());
457
-			// Verify we can share with this user
458
-			$groups = array_intersect(
459
-				$this->groupManager->getUserGroupIds($sharedBy),
460
-				$this->groupManager->getUserGroupIds($sharedWith)
461
-			);
462
-
463
-			// optional excluded groups
464
-			$excludedGroups = $this->shareWithGroupMembersOnlyExcludeGroupsList();
465
-			$groups = array_diff($groups, $excludedGroups);
466
-
467
-			if (empty($groups)) {
468
-				throw new \Exception($this->l->t('Sharing is only allowed with group members'));
469
-			}
470
-		}
471
-
472
-		/*
66
+    private ?IL10N $l;
67
+    private LegacyHooks $legacyHooks;
68
+
69
+    public function __construct(
70
+        private LoggerInterface $logger,
71
+        private IConfig $config,
72
+        private ISecureRandom $secureRandom,
73
+        private IHasher $hasher,
74
+        private IMountManager $mountManager,
75
+        private IGroupManager $groupManager,
76
+        private IFactory $l10nFactory,
77
+        private IProviderFactory $factory,
78
+        private IUserManager $userManager,
79
+        private IRootFolder $rootFolder,
80
+        private IMailer $mailer,
81
+        private IURLGenerator $urlGenerator,
82
+        private \OC_Defaults $defaults,
83
+        private IEventDispatcher $dispatcher,
84
+        private IUserSession $userSession,
85
+        private KnownUserService $knownUserService,
86
+        private ShareDisableChecker $shareDisableChecker,
87
+        private IDateTimeZone $dateTimeZone,
88
+        private IAppConfig $appConfig,
89
+    ) {
90
+        $this->l = $this->l10nFactory->get('lib');
91
+        // The constructor of LegacyHooks registers the listeners of share events
92
+        // do not remove if those are not properly migrated
93
+        $this->legacyHooks = new LegacyHooks($this->dispatcher);
94
+    }
95
+
96
+    /**
97
+     * Convert from a full share id to a tuple (providerId, shareId)
98
+     *
99
+     * @param string $id
100
+     * @return string[]
101
+     */
102
+    private function splitFullId($id) {
103
+        return explode(':', $id, 2);
104
+    }
105
+
106
+    /**
107
+     * Verify if a password meets all requirements
108
+     *
109
+     * @param string $password
110
+     * @throws HintException
111
+     */
112
+    protected function verifyPassword($password) {
113
+        if ($password === null) {
114
+            // No password is set, check if this is allowed.
115
+            if ($this->shareApiLinkEnforcePassword()) {
116
+                throw new \InvalidArgumentException($this->l->t('Passwords are enforced for link and mail shares'));
117
+            }
118
+
119
+            return;
120
+        }
121
+
122
+        // Let others verify the password
123
+        try {
124
+            $event = new ValidatePasswordPolicyEvent($password, PasswordContext::SHARING);
125
+            $this->dispatcher->dispatchTyped($event);
126
+        } catch (HintException $e) {
127
+            /* Wrap in a 400 bad request error */
128
+            throw new HintException($e->getMessage(), $e->getHint(), 400, $e);
129
+        }
130
+    }
131
+
132
+    /**
133
+     * Check for generic requirements before creating a share
134
+     *
135
+     * @param IShare $share
136
+     * @throws \InvalidArgumentException
137
+     * @throws GenericShareException
138
+     *
139
+     * @suppress PhanUndeclaredClassMethod
140
+     */
141
+    protected function generalCreateChecks(IShare $share, bool $isUpdate = false) {
142
+        if ($share->getShareType() === IShare::TYPE_USER) {
143
+            // We expect a valid user as sharedWith for user shares
144
+            if (!$this->userManager->userExists($share->getSharedWith())) {
145
+                throw new \InvalidArgumentException($this->l->t('Share recipient is not a valid user'));
146
+            }
147
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
148
+            // We expect a valid group as sharedWith for group shares
149
+            if (!$this->groupManager->groupExists($share->getSharedWith())) {
150
+                throw new \InvalidArgumentException($this->l->t('Share recipient is not a valid group'));
151
+            }
152
+        } elseif ($share->getShareType() === IShare::TYPE_LINK) {
153
+            // No check for TYPE_EMAIL here as we have a recipient for them
154
+            if ($share->getSharedWith() !== null) {
155
+                throw new \InvalidArgumentException($this->l->t('Share recipient should be empty'));
156
+            }
157
+        } elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
158
+            if ($share->getSharedWith() === null) {
159
+                throw new \InvalidArgumentException($this->l->t('Share recipient should not be empty'));
160
+            }
161
+        } elseif ($share->getShareType() === IShare::TYPE_REMOTE) {
162
+            if ($share->getSharedWith() === null) {
163
+                throw new \InvalidArgumentException($this->l->t('Share recipient should not be empty'));
164
+            }
165
+        } elseif ($share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
166
+            if ($share->getSharedWith() === null) {
167
+                throw new \InvalidArgumentException($this->l->t('Share recipient should not be empty'));
168
+            }
169
+        } elseif ($share->getShareType() === IShare::TYPE_CIRCLE) {
170
+            $circle = \OCA\Circles\Api\v1\Circles::detailsCircle($share->getSharedWith());
171
+            if ($circle === null) {
172
+                throw new \InvalidArgumentException($this->l->t('Share recipient is not a valid circle'));
173
+            }
174
+        } elseif ($share->getShareType() === IShare::TYPE_ROOM) {
175
+        } elseif ($share->getShareType() === IShare::TYPE_DECK) {
176
+        } elseif ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
177
+        } else {
178
+            // We cannot handle other types yet
179
+            throw new \InvalidArgumentException($this->l->t('Unknown share type'));
180
+        }
181
+
182
+        // Verify the initiator of the share is set
183
+        if ($share->getSharedBy() === null) {
184
+            throw new \InvalidArgumentException($this->l->t('Share initiator must be set'));
185
+        }
186
+
187
+        // Cannot share with yourself
188
+        if ($share->getShareType() === IShare::TYPE_USER
189
+            && $share->getSharedWith() === $share->getSharedBy()) {
190
+            throw new \InvalidArgumentException($this->l->t('Cannot share with yourself'));
191
+        }
192
+
193
+        // The path should be set
194
+        if ($share->getNode() === null) {
195
+            throw new \InvalidArgumentException($this->l->t('Shared path must be set'));
196
+        }
197
+
198
+        // And it should be a file or a folder
199
+        if (!($share->getNode() instanceof \OCP\Files\File)
200
+            && !($share->getNode() instanceof \OCP\Files\Folder)) {
201
+            throw new \InvalidArgumentException($this->l->t('Shared path must be either a file or a folder'));
202
+        }
203
+
204
+        // And you cannot share your rootfolder
205
+        if ($this->userManager->userExists($share->getSharedBy())) {
206
+            $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
207
+        } else {
208
+            $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
209
+        }
210
+        if ($userFolder->getId() === $share->getNode()->getId()) {
211
+            throw new \InvalidArgumentException($this->l->t('You cannot share your root folder'));
212
+        }
213
+
214
+        // Check if we actually have share permissions
215
+        if (!$share->getNode()->isShareable()) {
216
+            throw new GenericShareException($this->l->t('You are not allowed to share %s', [$share->getNode()->getName()]), code: 404);
217
+        }
218
+
219
+        // Permissions should be set
220
+        if ($share->getPermissions() === null) {
221
+            throw new \InvalidArgumentException($this->l->t('Valid permissions are required for sharing'));
222
+        }
223
+
224
+        // Permissions must be valid
225
+        if ($share->getPermissions() < 0 || $share->getPermissions() > \OCP\Constants::PERMISSION_ALL) {
226
+            throw new \InvalidArgumentException($this->l->t('Valid permissions are required for sharing'));
227
+        }
228
+
229
+        // Single file shares should never have delete or create permissions
230
+        if (($share->getNode() instanceof File)
231
+            && (($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_DELETE)) !== 0)) {
232
+            throw new \InvalidArgumentException($this->l->t('File shares cannot have create or delete permissions'));
233
+        }
234
+
235
+        $permissions = 0;
236
+        $nodesForUser = $userFolder->getById($share->getNodeId());
237
+        foreach ($nodesForUser as $node) {
238
+            if ($node->getInternalPath() === '' && !$node->getMountPoint() instanceof MoveableMount) {
239
+                // for the root of non-movable mount, the permissions we see if limited by the mount itself,
240
+                // so we instead use the "raw" permissions from the storage
241
+                $permissions |= $node->getStorage()->getPermissions('');
242
+            } else {
243
+                $permissions |= $node->getPermissions();
244
+            }
245
+        }
246
+
247
+        // Check that we do not share with more permissions than we have
248
+        if ($share->getPermissions() & ~$permissions) {
249
+            $path = $userFolder->getRelativePath($share->getNode()->getPath());
250
+            throw new GenericShareException($this->l->t('Cannot increase permissions of %s', [$path]), code: 404);
251
+        }
252
+
253
+        // Check that read permissions are always set
254
+        // Link shares are allowed to have no read permissions to allow upload to hidden folders
255
+        $noReadPermissionRequired = $share->getShareType() === IShare::TYPE_LINK
256
+            || $share->getShareType() === IShare::TYPE_EMAIL;
257
+        if (!$noReadPermissionRequired
258
+            && ($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
259
+            throw new \InvalidArgumentException($this->l->t('Shares need at least read permissions'));
260
+        }
261
+
262
+        if ($share->getNode() instanceof \OCP\Files\File) {
263
+            if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
264
+                throw new GenericShareException($this->l->t('Files cannot be shared with delete permissions'));
265
+            }
266
+            if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
267
+                throw new GenericShareException($this->l->t('Files cannot be shared with create permissions'));
268
+            }
269
+        }
270
+    }
271
+
272
+    /**
273
+     * Validate if the expiration date fits the system settings
274
+     *
275
+     * @param IShare $share The share to validate the expiration date of
276
+     * @return IShare The modified share object
277
+     * @throws GenericShareException
278
+     * @throws \InvalidArgumentException
279
+     * @throws \Exception
280
+     */
281
+    protected function validateExpirationDateInternal(IShare $share) {
282
+        $isRemote = $share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP;
283
+
284
+        $expirationDate = $share->getExpirationDate();
285
+
286
+        if ($isRemote) {
287
+            $defaultExpireDate = $this->shareApiRemoteDefaultExpireDate();
288
+            $defaultExpireDays = $this->shareApiRemoteDefaultExpireDays();
289
+            $configProp = 'remote_defaultExpDays';
290
+            $isEnforced = $this->shareApiRemoteDefaultExpireDateEnforced();
291
+        } else {
292
+            $defaultExpireDate = $this->shareApiInternalDefaultExpireDate();
293
+            $defaultExpireDays = $this->shareApiInternalDefaultExpireDays();
294
+            $configProp = 'internal_defaultExpDays';
295
+            $isEnforced = $this->shareApiInternalDefaultExpireDateEnforced();
296
+        }
297
+
298
+        // If $expirationDate is falsy, noExpirationDate is true and expiration not enforced
299
+        // Then skip expiration date validation as null is accepted
300
+        if (!$share->getNoExpirationDate() || $isEnforced) {
301
+            if ($expirationDate !== null) {
302
+                $expirationDate->setTimezone($this->dateTimeZone->getTimeZone());
303
+                $expirationDate->setTime(0, 0, 0);
304
+
305
+                $date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
306
+                $date->setTime(0, 0, 0);
307
+                if ($date >= $expirationDate) {
308
+                    throw new GenericShareException($this->l->t('Expiration date is in the past'), code: 404);
309
+                }
310
+            }
311
+
312
+            // If expiredate is empty set a default one if there is a default
313
+            $fullId = null;
314
+            try {
315
+                $fullId = $share->getFullId();
316
+            } catch (\UnexpectedValueException $e) {
317
+                // This is a new share
318
+            }
319
+
320
+            if ($fullId === null && $expirationDate === null && $defaultExpireDate) {
321
+                $expirationDate = new \DateTime('now', $this->dateTimeZone->getTimeZone());
322
+                $expirationDate->setTime(0, 0, 0);
323
+                $days = (int)$this->config->getAppValue('core', $configProp, (string)$defaultExpireDays);
324
+                if ($days > $defaultExpireDays) {
325
+                    $days = $defaultExpireDays;
326
+                }
327
+                $expirationDate->add(new \DateInterval('P' . $days . 'D'));
328
+            }
329
+
330
+            // If we enforce the expiration date check that is does not exceed
331
+            if ($isEnforced) {
332
+                if (empty($expirationDate)) {
333
+                    throw new \InvalidArgumentException($this->l->t('Expiration date is enforced'));
334
+                }
335
+
336
+                $date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
337
+                $date->setTime(0, 0, 0);
338
+                $date->add(new \DateInterval('P' . $defaultExpireDays . 'D'));
339
+                if ($date < $expirationDate) {
340
+                    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);
341
+                }
342
+            }
343
+        }
344
+
345
+        $accepted = true;
346
+        $message = '';
347
+        \OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
348
+            'expirationDate' => &$expirationDate,
349
+            'accepted' => &$accepted,
350
+            'message' => &$message,
351
+            'passwordSet' => $share->getPassword() !== null,
352
+        ]);
353
+
354
+        if (!$accepted) {
355
+            throw new \Exception($message);
356
+        }
357
+
358
+        $share->setExpirationDate($expirationDate);
359
+
360
+        return $share;
361
+    }
362
+
363
+    /**
364
+     * Validate if the expiration date fits the system settings
365
+     *
366
+     * @param IShare $share The share to validate the expiration date of
367
+     * @return IShare The modified share object
368
+     * @throws GenericShareException
369
+     * @throws \InvalidArgumentException
370
+     * @throws \Exception
371
+     */
372
+    protected function validateExpirationDateLink(IShare $share) {
373
+        $expirationDate = $share->getExpirationDate();
374
+        $isEnforced = $this->shareApiLinkDefaultExpireDateEnforced();
375
+
376
+        // If $expirationDate is falsy, noExpirationDate is true and expiration not enforced
377
+        // Then skip expiration date validation as null is accepted
378
+        if (!($share->getNoExpirationDate() && !$isEnforced)) {
379
+            if ($expirationDate !== null) {
380
+                $expirationDate->setTimezone($this->dateTimeZone->getTimeZone());
381
+                $expirationDate->setTime(0, 0, 0);
382
+
383
+                $date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
384
+                $date->setTime(0, 0, 0);
385
+                if ($date >= $expirationDate) {
386
+                    throw new GenericShareException($this->l->t('Expiration date is in the past'), code: 404);
387
+                }
388
+            }
389
+
390
+            // If expiredate is empty set a default one if there is a default
391
+            $fullId = null;
392
+            try {
393
+                $fullId = $share->getFullId();
394
+            } catch (\UnexpectedValueException $e) {
395
+                // This is a new share
396
+            }
397
+
398
+            if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
399
+                $expirationDate = new \DateTime('now', $this->dateTimeZone->getTimeZone());
400
+                $expirationDate->setTime(0, 0, 0);
401
+
402
+                $days = (int)$this->config->getAppValue('core', 'link_defaultExpDays', (string)$this->shareApiLinkDefaultExpireDays());
403
+                if ($days > $this->shareApiLinkDefaultExpireDays()) {
404
+                    $days = $this->shareApiLinkDefaultExpireDays();
405
+                }
406
+                $expirationDate->add(new \DateInterval('P' . $days . 'D'));
407
+            }
408
+
409
+            // If we enforce the expiration date check that is does not exceed
410
+            if ($isEnforced) {
411
+                if (empty($expirationDate)) {
412
+                    throw new \InvalidArgumentException($this->l->t('Expiration date is enforced'));
413
+                }
414
+
415
+                $date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
416
+                $date->setTime(0, 0, 0);
417
+                $date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
418
+                if ($date < $expirationDate) {
419
+                    throw new GenericShareException(
420
+                        $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()),
421
+                        code: 404,
422
+                    );
423
+                }
424
+            }
425
+
426
+        }
427
+
428
+        $accepted = true;
429
+        $message = '';
430
+        \OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
431
+            'expirationDate' => &$expirationDate,
432
+            'accepted' => &$accepted,
433
+            'message' => &$message,
434
+            'passwordSet' => $share->getPassword() !== null,
435
+        ]);
436
+
437
+        if (!$accepted) {
438
+            throw new \Exception($message);
439
+        }
440
+
441
+        $share->setExpirationDate($expirationDate);
442
+
443
+        return $share;
444
+    }
445
+
446
+    /**
447
+     * Check for pre share requirements for user shares
448
+     *
449
+     * @param IShare $share
450
+     * @throws \Exception
451
+     */
452
+    protected function userCreateChecks(IShare $share) {
453
+        // Check if we can share with group members only
454
+        if ($this->shareWithGroupMembersOnly()) {
455
+            $sharedBy = $this->userManager->get($share->getSharedBy());
456
+            $sharedWith = $this->userManager->get($share->getSharedWith());
457
+            // Verify we can share with this user
458
+            $groups = array_intersect(
459
+                $this->groupManager->getUserGroupIds($sharedBy),
460
+                $this->groupManager->getUserGroupIds($sharedWith)
461
+            );
462
+
463
+            // optional excluded groups
464
+            $excludedGroups = $this->shareWithGroupMembersOnlyExcludeGroupsList();
465
+            $groups = array_diff($groups, $excludedGroups);
466
+
467
+            if (empty($groups)) {
468
+                throw new \Exception($this->l->t('Sharing is only allowed with group members'));
469
+            }
470
+        }
471
+
472
+        /*
473 473
 		 * TODO: Could be costly, fix
474 474
 		 *
475 475
 		 * Also this is not what we want in the future.. then we want to squash identical shares.
476 476
 		 */
477
-		$provider = $this->factory->getProviderForType(IShare::TYPE_USER);
478
-		$existingShares = $provider->getSharesByPath($share->getNode());
479
-		foreach ($existingShares as $existingShare) {
480
-			// Ignore if it is the same share
481
-			try {
482
-				if ($existingShare->getFullId() === $share->getFullId()) {
483
-					continue;
484
-				}
485
-			} catch (\UnexpectedValueException $e) {
486
-				//Shares are not identical
487
-			}
488
-
489
-			// Identical share already exists
490
-			if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
491
-				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);
492
-			}
493
-
494
-			// The share is already shared with this user via a group share
495
-			if ($existingShare->getShareType() === IShare::TYPE_GROUP) {
496
-				$group = $this->groupManager->get($existingShare->getSharedWith());
497
-				if (!is_null($group)) {
498
-					$user = $this->userManager->get($share->getSharedWith());
499
-
500
-					if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) {
501
-						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);
502
-					}
503
-				}
504
-			}
505
-		}
506
-	}
507
-
508
-	/**
509
-	 * Check for pre share requirements for group shares
510
-	 *
511
-	 * @param IShare $share
512
-	 * @throws \Exception
513
-	 */
514
-	protected function groupCreateChecks(IShare $share) {
515
-		// Verify group shares are allowed
516
-		if (!$this->allowGroupSharing()) {
517
-			throw new \Exception($this->l->t('Group sharing is now allowed'));
518
-		}
519
-
520
-		// Verify if the user can share with this group
521
-		if ($this->shareWithGroupMembersOnly()) {
522
-			$sharedBy = $this->userManager->get($share->getSharedBy());
523
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
524
-
525
-			// optional excluded groups
526
-			$excludedGroups = $this->shareWithGroupMembersOnlyExcludeGroupsList();
527
-			if (is_null($sharedWith) || in_array($share->getSharedWith(), $excludedGroups) || !$sharedWith->inGroup($sharedBy)) {
528
-				throw new \Exception($this->l->t('Sharing is only allowed within your own groups'));
529
-			}
530
-		}
531
-
532
-		/*
477
+        $provider = $this->factory->getProviderForType(IShare::TYPE_USER);
478
+        $existingShares = $provider->getSharesByPath($share->getNode());
479
+        foreach ($existingShares as $existingShare) {
480
+            // Ignore if it is the same share
481
+            try {
482
+                if ($existingShare->getFullId() === $share->getFullId()) {
483
+                    continue;
484
+                }
485
+            } catch (\UnexpectedValueException $e) {
486
+                //Shares are not identical
487
+            }
488
+
489
+            // Identical share already exists
490
+            if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
491
+                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);
492
+            }
493
+
494
+            // The share is already shared with this user via a group share
495
+            if ($existingShare->getShareType() === IShare::TYPE_GROUP) {
496
+                $group = $this->groupManager->get($existingShare->getSharedWith());
497
+                if (!is_null($group)) {
498
+                    $user = $this->userManager->get($share->getSharedWith());
499
+
500
+                    if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) {
501
+                        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);
502
+                    }
503
+                }
504
+            }
505
+        }
506
+    }
507
+
508
+    /**
509
+     * Check for pre share requirements for group shares
510
+     *
511
+     * @param IShare $share
512
+     * @throws \Exception
513
+     */
514
+    protected function groupCreateChecks(IShare $share) {
515
+        // Verify group shares are allowed
516
+        if (!$this->allowGroupSharing()) {
517
+            throw new \Exception($this->l->t('Group sharing is now allowed'));
518
+        }
519
+
520
+        // Verify if the user can share with this group
521
+        if ($this->shareWithGroupMembersOnly()) {
522
+            $sharedBy = $this->userManager->get($share->getSharedBy());
523
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
524
+
525
+            // optional excluded groups
526
+            $excludedGroups = $this->shareWithGroupMembersOnlyExcludeGroupsList();
527
+            if (is_null($sharedWith) || in_array($share->getSharedWith(), $excludedGroups) || !$sharedWith->inGroup($sharedBy)) {
528
+                throw new \Exception($this->l->t('Sharing is only allowed within your own groups'));
529
+            }
530
+        }
531
+
532
+        /*
533 533
 		 * TODO: Could be costly, fix
534 534
 		 *
535 535
 		 * Also this is not what we want in the future.. then we want to squash identical shares.
536 536
 		 */
537
-		$provider = $this->factory->getProviderForType(IShare::TYPE_GROUP);
538
-		$existingShares = $provider->getSharesByPath($share->getNode());
539
-		foreach ($existingShares as $existingShare) {
540
-			try {
541
-				if ($existingShare->getFullId() === $share->getFullId()) {
542
-					continue;
543
-				}
544
-			} catch (\UnexpectedValueException $e) {
545
-				//It is a new share so just continue
546
-			}
547
-
548
-			if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
549
-				throw new AlreadySharedException($this->l->t('Path is already shared with this group'), $existingShare);
550
-			}
551
-		}
552
-	}
553
-
554
-	/**
555
-	 * Check for pre share requirements for link shares
556
-	 *
557
-	 * @param IShare $share
558
-	 * @throws \Exception
559
-	 */
560
-	protected function linkCreateChecks(IShare $share) {
561
-		// Are link shares allowed?
562
-		if (!$this->shareApiAllowLinks()) {
563
-			throw new \Exception($this->l->t('Link sharing is not allowed'));
564
-		}
565
-
566
-		// Check if public upload is allowed
567
-		if ($share->getNodeType() === 'folder' && !$this->shareApiLinkAllowPublicUpload()
568
-			&& ($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
569
-			throw new \InvalidArgumentException($this->l->t('Public upload is not allowed'));
570
-		}
571
-	}
572
-
573
-	/**
574
-	 * To make sure we don't get invisible link shares we set the parent
575
-	 * of a link if it is a reshare. This is a quick word around
576
-	 * until we can properly display multiple link shares in the UI
577
-	 *
578
-	 * See: https://github.com/owncloud/core/issues/22295
579
-	 *
580
-	 * FIXME: Remove once multiple link shares can be properly displayed
581
-	 *
582
-	 * @param IShare $share
583
-	 */
584
-	protected function setLinkParent(IShare $share) {
585
-		$storage = $share->getNode()->getStorage();
586
-		if ($storage->instanceOfStorage(SharedStorage::class)) {
587
-			/** @var \OCA\Files_Sharing\SharedStorage $storage */
588
-			$share->setParent((int)$storage->getShareId());
589
-		}
590
-	}
591
-
592
-	/**
593
-	 * @param File|Folder $path
594
-	 */
595
-	protected function pathCreateChecks($path) {
596
-		// Make sure that we do not share a path that contains a shared mountpoint
597
-		if ($path instanceof \OCP\Files\Folder) {
598
-			$mounts = $this->mountManager->findIn($path->getPath());
599
-			foreach ($mounts as $mount) {
600
-				if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
601
-					// Using a flat sharing model ensures the file owner can always see who has access.
602
-					// Allowing parent folder sharing would require tracking inherited access, which adds complexity
603
-					// and hurts performance/scalability.
604
-					// So we forbid sharing a parent folder of a share you received.
605
-					throw new \InvalidArgumentException($this->l->t('You cannot share a folder that contains other shares'));
606
-				}
607
-			}
608
-		}
609
-	}
610
-
611
-	/**
612
-	 * Check if the user that is sharing can actually share
613
-	 *
614
-	 * @param IShare $share
615
-	 * @throws \Exception
616
-	 */
617
-	protected function canShare(IShare $share) {
618
-		if (!$this->shareApiEnabled()) {
619
-			throw new \Exception($this->l->t('Sharing is disabled'));
620
-		}
621
-
622
-		if ($this->sharingDisabledForUser($share->getSharedBy())) {
623
-			throw new \Exception($this->l->t('Sharing is disabled for you'));
624
-		}
625
-	}
626
-
627
-	/**
628
-	 * Share a path
629
-	 *
630
-	 * @param IShare $share
631
-	 * @return IShare The share object
632
-	 * @throws \Exception
633
-	 *
634
-	 * TODO: handle link share permissions or check them
635
-	 */
636
-	public function createShare(IShare $share) {
637
-		$this->canShare($share);
638
-
639
-		$this->generalCreateChecks($share);
640
-
641
-		// Verify if there are any issues with the path
642
-		$this->pathCreateChecks($share->getNode());
643
-
644
-		/*
537
+        $provider = $this->factory->getProviderForType(IShare::TYPE_GROUP);
538
+        $existingShares = $provider->getSharesByPath($share->getNode());
539
+        foreach ($existingShares as $existingShare) {
540
+            try {
541
+                if ($existingShare->getFullId() === $share->getFullId()) {
542
+                    continue;
543
+                }
544
+            } catch (\UnexpectedValueException $e) {
545
+                //It is a new share so just continue
546
+            }
547
+
548
+            if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
549
+                throw new AlreadySharedException($this->l->t('Path is already shared with this group'), $existingShare);
550
+            }
551
+        }
552
+    }
553
+
554
+    /**
555
+     * Check for pre share requirements for link shares
556
+     *
557
+     * @param IShare $share
558
+     * @throws \Exception
559
+     */
560
+    protected function linkCreateChecks(IShare $share) {
561
+        // Are link shares allowed?
562
+        if (!$this->shareApiAllowLinks()) {
563
+            throw new \Exception($this->l->t('Link sharing is not allowed'));
564
+        }
565
+
566
+        // Check if public upload is allowed
567
+        if ($share->getNodeType() === 'folder' && !$this->shareApiLinkAllowPublicUpload()
568
+            && ($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
569
+            throw new \InvalidArgumentException($this->l->t('Public upload is not allowed'));
570
+        }
571
+    }
572
+
573
+    /**
574
+     * To make sure we don't get invisible link shares we set the parent
575
+     * of a link if it is a reshare. This is a quick word around
576
+     * until we can properly display multiple link shares in the UI
577
+     *
578
+     * See: https://github.com/owncloud/core/issues/22295
579
+     *
580
+     * FIXME: Remove once multiple link shares can be properly displayed
581
+     *
582
+     * @param IShare $share
583
+     */
584
+    protected function setLinkParent(IShare $share) {
585
+        $storage = $share->getNode()->getStorage();
586
+        if ($storage->instanceOfStorage(SharedStorage::class)) {
587
+            /** @var \OCA\Files_Sharing\SharedStorage $storage */
588
+            $share->setParent((int)$storage->getShareId());
589
+        }
590
+    }
591
+
592
+    /**
593
+     * @param File|Folder $path
594
+     */
595
+    protected function pathCreateChecks($path) {
596
+        // Make sure that we do not share a path that contains a shared mountpoint
597
+        if ($path instanceof \OCP\Files\Folder) {
598
+            $mounts = $this->mountManager->findIn($path->getPath());
599
+            foreach ($mounts as $mount) {
600
+                if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
601
+                    // Using a flat sharing model ensures the file owner can always see who has access.
602
+                    // Allowing parent folder sharing would require tracking inherited access, which adds complexity
603
+                    // and hurts performance/scalability.
604
+                    // So we forbid sharing a parent folder of a share you received.
605
+                    throw new \InvalidArgumentException($this->l->t('You cannot share a folder that contains other shares'));
606
+                }
607
+            }
608
+        }
609
+    }
610
+
611
+    /**
612
+     * Check if the user that is sharing can actually share
613
+     *
614
+     * @param IShare $share
615
+     * @throws \Exception
616
+     */
617
+    protected function canShare(IShare $share) {
618
+        if (!$this->shareApiEnabled()) {
619
+            throw new \Exception($this->l->t('Sharing is disabled'));
620
+        }
621
+
622
+        if ($this->sharingDisabledForUser($share->getSharedBy())) {
623
+            throw new \Exception($this->l->t('Sharing is disabled for you'));
624
+        }
625
+    }
626
+
627
+    /**
628
+     * Share a path
629
+     *
630
+     * @param IShare $share
631
+     * @return IShare The share object
632
+     * @throws \Exception
633
+     *
634
+     * TODO: handle link share permissions or check them
635
+     */
636
+    public function createShare(IShare $share) {
637
+        $this->canShare($share);
638
+
639
+        $this->generalCreateChecks($share);
640
+
641
+        // Verify if there are any issues with the path
642
+        $this->pathCreateChecks($share->getNode());
643
+
644
+        /*
645 645
 		 * On creation of a share the owner is always the owner of the path
646 646
 		 * Except for mounted federated shares.
647 647
 		 */
648
-		$storage = $share->getNode()->getStorage();
649
-		if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
650
-			$parent = $share->getNode()->getParent();
651
-			while ($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
652
-				$parent = $parent->getParent();
653
-			}
654
-			$share->setShareOwner($parent->getOwner()->getUID());
655
-		} else {
656
-			if ($share->getNode()->getOwner()) {
657
-				$share->setShareOwner($share->getNode()->getOwner()->getUID());
658
-			} else {
659
-				$share->setShareOwner($share->getSharedBy());
660
-			}
661
-		}
662
-
663
-		try {
664
-			// Verify share type
665
-			if ($share->getShareType() === IShare::TYPE_USER) {
666
-				$this->userCreateChecks($share);
667
-
668
-				// Verify the expiration date
669
-				$share = $this->validateExpirationDateInternal($share);
670
-			} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
671
-				$this->groupCreateChecks($share);
672
-
673
-				// Verify the expiration date
674
-				$share = $this->validateExpirationDateInternal($share);
675
-			} elseif ($share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
676
-				// Verify the expiration date
677
-				$share = $this->validateExpirationDateInternal($share);
678
-			} elseif ($share->getShareType() === IShare::TYPE_LINK
679
-				|| $share->getShareType() === IShare::TYPE_EMAIL) {
680
-				$this->linkCreateChecks($share);
681
-				$this->setLinkParent($share);
682
-
683
-				$token = $this->generateToken();
684
-				// Set the unique token
685
-				$share->setToken($token);
686
-
687
-				// Verify the expiration date
688
-				$share = $this->validateExpirationDateLink($share);
689
-
690
-				// Verify the password
691
-				$this->verifyPassword($share->getPassword());
692
-
693
-				// If a password is set. Hash it!
694
-				if ($share->getShareType() === IShare::TYPE_LINK
695
-					&& $share->getPassword() !== null) {
696
-					$share->setPassword($this->hasher->hash($share->getPassword()));
697
-				}
698
-			}
699
-
700
-			// Cannot share with the owner
701
-			if ($share->getShareType() === IShare::TYPE_USER
702
-				&& $share->getSharedWith() === $share->getShareOwner()) {
703
-				throw new \InvalidArgumentException($this->l->t('Cannot share with the share owner'));
704
-			}
705
-
706
-			// Generate the target
707
-			$shareFolder = $this->config->getSystemValue('share_folder', '/');
708
-			if ($share->getShareType() === IShare::TYPE_USER) {
709
-				$allowCustomShareFolder = $this->config->getSystemValueBool('sharing.allow_custom_share_folder', true);
710
-				if ($allowCustomShareFolder) {
711
-					$shareFolder = $this->config->getUserValue($share->getSharedWith(), Application::APP_ID, 'share_folder', $shareFolder);
712
-				}
713
-			}
714
-
715
-			$target = $shareFolder . '/' . $share->getNode()->getName();
716
-			$target = \OC\Files\Filesystem::normalizePath($target);
717
-			$share->setTarget($target);
718
-
719
-			// Pre share event
720
-			$event = new Share\Events\BeforeShareCreatedEvent($share);
721
-			$this->dispatchEvent($event, 'before share created');
722
-			if ($event->isPropagationStopped() && $event->getError()) {
723
-				throw new \Exception($event->getError());
724
-			}
725
-
726
-			$oldShare = $share;
727
-			$provider = $this->factory->getProviderForType($share->getShareType());
728
-			$share = $provider->create($share);
729
-
730
-			// Reuse the node we already have
731
-			$share->setNode($oldShare->getNode());
732
-
733
-			// Reset the target if it is null for the new share
734
-			if ($share->getTarget() === '') {
735
-				$share->setTarget($target);
736
-			}
737
-		} catch (AlreadySharedException $e) {
738
-			// If a share for the same target already exists, dont create a new one,
739
-			// but do trigger the hooks and notifications again
740
-			$oldShare = $share;
741
-
742
-			// Reuse the node we already have
743
-			$share = $e->getExistingShare();
744
-			$share->setNode($oldShare->getNode());
745
-		}
746
-
747
-		// Post share event
748
-		$this->dispatchEvent(new ShareCreatedEvent($share), 'share created');
749
-
750
-		// Send email if needed
751
-		if ($this->config->getSystemValueBool('sharing.enable_share_mail', true)) {
752
-			if ($share->getMailSend()) {
753
-				$provider = $this->factory->getProviderForType($share->getShareType());
754
-				if ($provider instanceof IShareProviderWithNotification) {
755
-					$provider->sendMailNotification($share);
756
-				} else {
757
-					$this->logger->debug('Share notification not sent because the provider does not support it.', ['app' => 'share']);
758
-				}
759
-			} else {
760
-				$this->logger->debug('Share notification not sent because mailsend is false.', ['app' => 'share']);
761
-			}
762
-		} else {
763
-			$this->logger->debug('Share notification not sent because sharing notification emails is disabled.', ['app' => 'share']);
764
-		}
765
-
766
-		return $share;
767
-	}
768
-
769
-	/**
770
-	 * Update a share
771
-	 *
772
-	 * @param IShare $share
773
-	 * @return IShare The share object
774
-	 * @throws \InvalidArgumentException
775
-	 * @throws HintException
776
-	 */
777
-	public function updateShare(IShare $share, bool $onlyValid = true) {
778
-		$expirationDateUpdated = false;
779
-
780
-		$this->canShare($share);
781
-
782
-		try {
783
-			$originalShare = $this->getShareById($share->getFullId(), onlyValid: $onlyValid);
784
-		} catch (\UnexpectedValueException $e) {
785
-			throw new \InvalidArgumentException($this->l->t('Share does not have a full ID'));
786
-		}
787
-
788
-		// We cannot change the share type!
789
-		if ($share->getShareType() !== $originalShare->getShareType()) {
790
-			throw new \InvalidArgumentException($this->l->t('Cannot change share type'));
791
-		}
792
-
793
-		// We can only change the recipient on user shares
794
-		if ($share->getSharedWith() !== $originalShare->getSharedWith()
795
-			&& $share->getShareType() !== IShare::TYPE_USER) {
796
-			throw new \InvalidArgumentException($this->l->t('Can only update recipient on user shares'));
797
-		}
798
-
799
-		// Cannot share with the owner
800
-		if ($share->getShareType() === IShare::TYPE_USER
801
-			&& $share->getSharedWith() === $share->getShareOwner()) {
802
-			throw new \InvalidArgumentException($this->l->t('Cannot share with the share owner'));
803
-		}
804
-
805
-		$this->generalCreateChecks($share, true);
806
-
807
-		if ($share->getShareType() === IShare::TYPE_USER) {
808
-			$this->userCreateChecks($share);
809
-
810
-			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
811
-				// Verify the expiration date
812
-				$this->validateExpirationDateInternal($share);
813
-				$expirationDateUpdated = true;
814
-			}
815
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
816
-			$this->groupCreateChecks($share);
817
-
818
-			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
819
-				// Verify the expiration date
820
-				$this->validateExpirationDateInternal($share);
821
-				$expirationDateUpdated = true;
822
-			}
823
-		} elseif ($share->getShareType() === IShare::TYPE_LINK
824
-			|| $share->getShareType() === IShare::TYPE_EMAIL) {
825
-			$this->linkCreateChecks($share);
826
-
827
-			// The new password is not set again if it is the same as the old
828
-			// one, unless when switching from sending by Talk to sending by
829
-			// mail.
830
-			$plainTextPassword = $share->getPassword();
831
-			$updatedPassword = $this->updateSharePasswordIfNeeded($share, $originalShare);
832
-
833
-			/**
834
-			 * Cannot enable the getSendPasswordByTalk if there is no password set
835
-			 */
836
-			if (empty($plainTextPassword) && $share->getSendPasswordByTalk()) {
837
-				throw new \InvalidArgumentException($this->l->t('Cannot enable sending the password by Talk with an empty password'));
838
-			}
839
-
840
-			/**
841
-			 * If we're in a mail share, we need to force a password change
842
-			 * as either the user is not aware of the password or is already (received by mail)
843
-			 * Thus the SendPasswordByTalk feature would not make sense
844
-			 */
845
-			if (!$updatedPassword && $share->getShareType() === IShare::TYPE_EMAIL) {
846
-				if (!$originalShare->getSendPasswordByTalk() && $share->getSendPasswordByTalk()) {
847
-					throw new \InvalidArgumentException($this->l->t('Cannot enable sending the password by Talk without setting a new password'));
848
-				}
849
-				if ($originalShare->getSendPasswordByTalk() && !$share->getSendPasswordByTalk()) {
850
-					throw new \InvalidArgumentException($this->l->t('Cannot disable sending the password by Talk without setting a new password'));
851
-				}
852
-			}
853
-
854
-			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
855
-				// Verify the expiration date
856
-				$this->validateExpirationDateLink($share);
857
-				$expirationDateUpdated = true;
858
-			}
859
-		} elseif ($share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
860
-			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
861
-				// Verify the expiration date
862
-				$this->validateExpirationDateInternal($share);
863
-				$expirationDateUpdated = true;
864
-			}
865
-		}
866
-
867
-		$this->pathCreateChecks($share->getNode());
868
-
869
-		// Now update the share!
870
-		$provider = $this->factory->getProviderForType($share->getShareType());
871
-		if ($share->getShareType() === IShare::TYPE_EMAIL) {
872
-			/** @var ShareByMailProvider $provider */
873
-			$share = $provider->update($share, $plainTextPassword);
874
-		} else {
875
-			$share = $provider->update($share);
876
-		}
877
-
878
-		if ($expirationDateUpdated === true) {
879
-			\OC_Hook::emit(Share::class, 'post_set_expiration_date', [
880
-				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
881
-				'itemSource' => $share->getNode()->getId(),
882
-				'date' => $share->getExpirationDate(),
883
-				'uidOwner' => $share->getSharedBy(),
884
-			]);
885
-		}
886
-
887
-		if ($share->getPassword() !== $originalShare->getPassword()) {
888
-			\OC_Hook::emit(Share::class, 'post_update_password', [
889
-				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
890
-				'itemSource' => $share->getNode()->getId(),
891
-				'uidOwner' => $share->getSharedBy(),
892
-				'token' => $share->getToken(),
893
-				'disabled' => is_null($share->getPassword()),
894
-			]);
895
-		}
896
-
897
-		if ($share->getPermissions() !== $originalShare->getPermissions()) {
898
-			if ($this->userManager->userExists($share->getShareOwner())) {
899
-				$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
900
-			} else {
901
-				$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
902
-			}
903
-			\OC_Hook::emit(Share::class, 'post_update_permissions', [
904
-				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
905
-				'itemSource' => $share->getNode()->getId(),
906
-				'shareType' => $share->getShareType(),
907
-				'shareWith' => $share->getSharedWith(),
908
-				'uidOwner' => $share->getSharedBy(),
909
-				'permissions' => $share->getPermissions(),
910
-				'attributes' => $share->getAttributes() !== null ? $share->getAttributes()->toArray() : null,
911
-				'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
912
-			]);
913
-		}
914
-
915
-		return $share;
916
-	}
917
-
918
-	/**
919
-	 * Accept a share.
920
-	 *
921
-	 * @param IShare $share
922
-	 * @param string $recipientId
923
-	 * @return IShare The share object
924
-	 * @throws \InvalidArgumentException Thrown if the provider does not implement `IShareProviderSupportsAccept`
925
-	 * @since 9.0.0
926
-	 */
927
-	public function acceptShare(IShare $share, string $recipientId): IShare {
928
-		[$providerId,] = $this->splitFullId($share->getFullId());
929
-		$provider = $this->factory->getProvider($providerId);
930
-
931
-		if (!($provider instanceof IShareProviderSupportsAccept)) {
932
-			throw new \InvalidArgumentException($this->l->t('Share provider does not support accepting'));
933
-		}
934
-		/** @var IShareProvider&IShareProviderSupportsAccept $provider */
935
-		$provider->acceptShare($share, $recipientId);
936
-
937
-		$event = new ShareAcceptedEvent($share);
938
-		$this->dispatchEvent($event, 'share accepted');
939
-
940
-		return $share;
941
-	}
942
-
943
-	/**
944
-	 * Updates the password of the given share if it is not the same as the
945
-	 * password of the original share.
946
-	 *
947
-	 * @param IShare $share the share to update its password.
948
-	 * @param IShare $originalShare the original share to compare its
949
-	 *                              password with.
950
-	 * @return boolean whether the password was updated or not.
951
-	 */
952
-	private function updateSharePasswordIfNeeded(IShare $share, IShare $originalShare) {
953
-		$passwordsAreDifferent = ($share->getPassword() !== $originalShare->getPassword())
954
-			&& (($share->getPassword() !== null && $originalShare->getPassword() === null)
955
-				|| ($share->getPassword() === null && $originalShare->getPassword() !== null)
956
-				|| ($share->getPassword() !== null && $originalShare->getPassword() !== null
957
-					&& !$this->hasher->verify($share->getPassword(), $originalShare->getPassword())));
958
-
959
-		// Password updated.
960
-		if ($passwordsAreDifferent) {
961
-			// Verify the password
962
-			$this->verifyPassword($share->getPassword());
963
-
964
-			// If a password is set. Hash it!
965
-			if (!empty($share->getPassword())) {
966
-				$share->setPassword($this->hasher->hash($share->getPassword()));
967
-				if ($share->getShareType() === IShare::TYPE_EMAIL) {
968
-					// Shares shared by email have temporary passwords
969
-					$this->setSharePasswordExpirationTime($share);
970
-				}
971
-
972
-				return true;
973
-			} else {
974
-				// Empty string and null are seen as NOT password protected
975
-				$share->setPassword(null);
976
-				if ($share->getShareType() === IShare::TYPE_EMAIL) {
977
-					$share->setPasswordExpirationTime(null);
978
-				}
979
-				return true;
980
-			}
981
-		} else {
982
-			// Reset the password to the original one, as it is either the same
983
-			// as the "new" password or a hashed version of it.
984
-			$share->setPassword($originalShare->getPassword());
985
-		}
986
-
987
-		return false;
988
-	}
989
-
990
-	/**
991
-	 * Set the share's password expiration time
992
-	 */
993
-	private function setSharePasswordExpirationTime(IShare $share): void {
994
-		if (!$this->config->getSystemValueBool('sharing.enable_mail_link_password_expiration', false)) {
995
-			// Sets password expiration date to NULL
996
-			$share->setPasswordExpirationTime();
997
-			return;
998
-		}
999
-		// Sets password expiration date
1000
-		$expirationTime = null;
1001
-		$now = new \DateTime();
1002
-		$expirationInterval = $this->config->getSystemValue('sharing.mail_link_password_expiration_interval', 3600);
1003
-		$expirationTime = $now->add(new \DateInterval('PT' . $expirationInterval . 'S'));
1004
-		$share->setPasswordExpirationTime($expirationTime);
1005
-	}
1006
-
1007
-
1008
-	/**
1009
-	 * Delete all the children of this share
1010
-	 *
1011
-	 * @param IShare $share
1012
-	 * @return IShare[] List of deleted shares
1013
-	 */
1014
-	protected function deleteChildren(IShare $share) {
1015
-		$deletedShares = [];
1016
-
1017
-		$provider = $this->factory->getProviderForType($share->getShareType());
1018
-
1019
-		foreach ($provider->getChildren($share) as $child) {
1020
-			$this->dispatchEvent(new BeforeShareDeletedEvent($child), 'before share deleted');
1021
-
1022
-			$deletedChildren = $this->deleteChildren($child);
1023
-			$deletedShares = array_merge($deletedShares, $deletedChildren);
1024
-
1025
-			$provider->delete($child);
1026
-			$this->dispatchEvent(new ShareDeletedEvent($child), 'share deleted');
1027
-			$deletedShares[] = $child;
1028
-		}
1029
-
1030
-		return $deletedShares;
1031
-	}
1032
-
1033
-	/** Promote re-shares into direct shares so that target user keeps access */
1034
-	protected function promoteReshares(IShare $share): void {
1035
-		try {
1036
-			$node = $share->getNode();
1037
-		} catch (NotFoundException) {
1038
-			/* Skip if node not found */
1039
-			return;
1040
-		}
1041
-
1042
-		$userIds = [];
1043
-
1044
-		if ($share->getShareType() === IShare::TYPE_USER) {
1045
-			$userIds[] = $share->getSharedWith();
1046
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
1047
-			$group = $this->groupManager->get($share->getSharedWith());
1048
-			$users = $group?->getUsers() ?? [];
1049
-
1050
-			foreach ($users as $user) {
1051
-				/* Skip share owner */
1052
-				if ($user->getUID() === $share->getShareOwner() || $user->getUID() === $share->getSharedBy()) {
1053
-					continue;
1054
-				}
1055
-				$userIds[] = $user->getUID();
1056
-			}
1057
-		} else {
1058
-			/* We only support user and group shares */
1059
-			return;
1060
-		}
1061
-
1062
-		$reshareRecords = [];
1063
-		$shareTypes = [
1064
-			IShare::TYPE_GROUP,
1065
-			IShare::TYPE_USER,
1066
-			IShare::TYPE_LINK,
1067
-			IShare::TYPE_REMOTE,
1068
-			IShare::TYPE_EMAIL,
1069
-		];
1070
-
1071
-		foreach ($userIds as $userId) {
1072
-			foreach ($shareTypes as $shareType) {
1073
-				try {
1074
-					$provider = $this->factory->getProviderForType($shareType);
1075
-				} catch (ProviderException $e) {
1076
-					continue;
1077
-				}
1078
-
1079
-				if ($node instanceof Folder) {
1080
-					/* We need to get all shares by this user to get subshares */
1081
-					$shares = $provider->getSharesBy($userId, $shareType, null, false, -1, 0);
1082
-
1083
-					foreach ($shares as $share) {
1084
-						try {
1085
-							$path = $share->getNode()->getPath();
1086
-						} catch (NotFoundException) {
1087
-							/* Ignore share of non-existing node */
1088
-							continue;
1089
-						}
1090
-						if ($node->getRelativePath($path) !== null) {
1091
-							/* If relative path is not null it means the shared node is the same or in a subfolder */
1092
-							$reshareRecords[] = $share;
1093
-						}
1094
-					}
1095
-				} else {
1096
-					$shares = $provider->getSharesBy($userId, $shareType, $node, false, -1, 0);
1097
-					foreach ($shares as $child) {
1098
-						$reshareRecords[] = $child;
1099
-					}
1100
-				}
1101
-			}
1102
-		}
1103
-
1104
-		foreach ($reshareRecords as $child) {
1105
-			try {
1106
-				/* Check if the share is still valid (means the resharer still has access to the file through another mean) */
1107
-				$this->generalCreateChecks($child);
1108
-			} catch (GenericShareException $e) {
1109
-				/* The check is invalid, promote it to a direct share from the sharer of parent share */
1110
-				$this->logger->debug('Promote reshare because of exception ' . $e->getMessage(), ['exception' => $e, 'fullId' => $child->getFullId()]);
1111
-				try {
1112
-					$child->setSharedBy($share->getSharedBy());
1113
-					$this->updateShare($child);
1114
-				} catch (GenericShareException|\InvalidArgumentException $e) {
1115
-					$this->logger->warning('Failed to promote reshare because of exception ' . $e->getMessage(), ['exception' => $e, 'fullId' => $child->getFullId()]);
1116
-				}
1117
-			}
1118
-		}
1119
-	}
1120
-
1121
-	/**
1122
-	 * Delete a share
1123
-	 *
1124
-	 * @param IShare $share
1125
-	 * @throws ShareNotFound
1126
-	 * @throws \InvalidArgumentException
1127
-	 */
1128
-	public function deleteShare(IShare $share) {
1129
-		try {
1130
-			$share->getFullId();
1131
-		} catch (\UnexpectedValueException $e) {
1132
-			throw new \InvalidArgumentException($this->l->t('Share does not have a full ID'));
1133
-		}
1134
-
1135
-		$this->dispatchEvent(new BeforeShareDeletedEvent($share), 'before share deleted');
1136
-
1137
-		// Get all children and delete them as well
1138
-		$this->deleteChildren($share);
1139
-
1140
-		// Do the actual delete
1141
-		$provider = $this->factory->getProviderForType($share->getShareType());
1142
-		$provider->delete($share);
1143
-
1144
-		$this->dispatchEvent(new ShareDeletedEvent($share), 'share deleted');
1145
-
1146
-		// Promote reshares of the deleted share
1147
-		$this->promoteReshares($share);
1148
-	}
1149
-
1150
-
1151
-	/**
1152
-	 * Unshare a file as the recipient.
1153
-	 * This can be different from a regular delete for example when one of
1154
-	 * the users in a groups deletes that share. But the provider should
1155
-	 * handle this.
1156
-	 *
1157
-	 * @param IShare $share
1158
-	 * @param string $recipientId
1159
-	 */
1160
-	public function deleteFromSelf(IShare $share, $recipientId) {
1161
-		[$providerId,] = $this->splitFullId($share->getFullId());
1162
-		$provider = $this->factory->getProvider($providerId);
1163
-
1164
-		$provider->deleteFromSelf($share, $recipientId);
1165
-		$event = new ShareDeletedFromSelfEvent($share);
1166
-		$this->dispatchEvent($event, 'leave share');
1167
-	}
1168
-
1169
-	public function restoreShare(IShare $share, string $recipientId): IShare {
1170
-		[$providerId,] = $this->splitFullId($share->getFullId());
1171
-		$provider = $this->factory->getProvider($providerId);
1172
-
1173
-		return $provider->restore($share, $recipientId);
1174
-	}
1175
-
1176
-	/**
1177
-	 * @inheritdoc
1178
-	 */
1179
-	public function moveShare(IShare $share, $recipientId) {
1180
-		if ($share->getShareType() === IShare::TYPE_LINK
1181
-			|| $share->getShareType() === IShare::TYPE_EMAIL) {
1182
-			throw new \InvalidArgumentException($this->l->t('Cannot change target of link share'));
1183
-		}
1184
-
1185
-		if ($share->getShareType() === IShare::TYPE_USER && $share->getSharedWith() !== $recipientId) {
1186
-			throw new \InvalidArgumentException($this->l->t('Invalid share recipient'));
1187
-		}
1188
-
1189
-		if ($share->getShareType() === IShare::TYPE_GROUP) {
1190
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
1191
-			if (is_null($sharedWith)) {
1192
-				throw new \InvalidArgumentException($this->l->t('Group "%s" does not exist', [$share->getSharedWith()]));
1193
-			}
1194
-			$recipient = $this->userManager->get($recipientId);
1195
-			if (!$sharedWith->inGroup($recipient)) {
1196
-				throw new \InvalidArgumentException($this->l->t('Invalid share recipient'));
1197
-			}
1198
-		}
1199
-
1200
-		[$providerId,] = $this->splitFullId($share->getFullId());
1201
-		$provider = $this->factory->getProvider($providerId);
1202
-
1203
-		return $provider->move($share, $recipientId);
1204
-	}
1205
-
1206
-	public function getSharesInFolder($userId, Folder $node, $reshares = false, $shallow = true) {
1207
-		$providers = $this->factory->getAllProviders();
1208
-		if (!$shallow) {
1209
-			throw new \Exception('non-shallow getSharesInFolder is no longer supported');
1210
-		}
1211
-
1212
-		$isOwnerless = $node->getMountPoint() instanceof IShareOwnerlessMount;
1213
-
1214
-		$shares = [];
1215
-		foreach ($providers as $provider) {
1216
-			if ($isOwnerless) {
1217
-				// If the provider does not implement the additional interface,
1218
-				// we lack a performant way of querying all shares and therefore ignore the provider.
1219
-				if ($provider instanceof IShareProviderSupportsAllSharesInFolder) {
1220
-					foreach ($provider->getAllSharesInFolder($node) as $fid => $data) {
1221
-						$shares[$fid] ??= [];
1222
-						$shares[$fid] = array_merge($shares[$fid], $data);
1223
-					}
1224
-				}
1225
-			} else {
1226
-				foreach ($provider->getSharesInFolder($userId, $node, $reshares) as $fid => $data) {
1227
-					$shares[$fid] ??= [];
1228
-					$shares[$fid] = array_merge($shares[$fid], $data);
1229
-				}
1230
-			}
1231
-		}
1232
-
1233
-		return $shares;
1234
-	}
1235
-
1236
-	/**
1237
-	 * @inheritdoc
1238
-	 */
1239
-	public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0, bool $onlyValid = true) {
1240
-		if ($path !== null
1241
-			&& !($path instanceof \OCP\Files\File)
1242
-			&& !($path instanceof \OCP\Files\Folder)) {
1243
-			throw new \InvalidArgumentException($this->l->t('Invalid path'));
1244
-		}
1245
-
1246
-		try {
1247
-			$provider = $this->factory->getProviderForType($shareType);
1248
-		} catch (ProviderException $e) {
1249
-			return [];
1250
-		}
1251
-
1252
-		if ($path?->getMountPoint() instanceof IShareOwnerlessMount) {
1253
-			$shares = array_filter($provider->getSharesByPath($path), static fn (IShare $share) => $share->getShareType() === $shareType);
1254
-		} else {
1255
-			$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1256
-		}
1257
-
1258
-		/*
648
+        $storage = $share->getNode()->getStorage();
649
+        if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
650
+            $parent = $share->getNode()->getParent();
651
+            while ($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
652
+                $parent = $parent->getParent();
653
+            }
654
+            $share->setShareOwner($parent->getOwner()->getUID());
655
+        } else {
656
+            if ($share->getNode()->getOwner()) {
657
+                $share->setShareOwner($share->getNode()->getOwner()->getUID());
658
+            } else {
659
+                $share->setShareOwner($share->getSharedBy());
660
+            }
661
+        }
662
+
663
+        try {
664
+            // Verify share type
665
+            if ($share->getShareType() === IShare::TYPE_USER) {
666
+                $this->userCreateChecks($share);
667
+
668
+                // Verify the expiration date
669
+                $share = $this->validateExpirationDateInternal($share);
670
+            } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
671
+                $this->groupCreateChecks($share);
672
+
673
+                // Verify the expiration date
674
+                $share = $this->validateExpirationDateInternal($share);
675
+            } elseif ($share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
676
+                // Verify the expiration date
677
+                $share = $this->validateExpirationDateInternal($share);
678
+            } elseif ($share->getShareType() === IShare::TYPE_LINK
679
+                || $share->getShareType() === IShare::TYPE_EMAIL) {
680
+                $this->linkCreateChecks($share);
681
+                $this->setLinkParent($share);
682
+
683
+                $token = $this->generateToken();
684
+                // Set the unique token
685
+                $share->setToken($token);
686
+
687
+                // Verify the expiration date
688
+                $share = $this->validateExpirationDateLink($share);
689
+
690
+                // Verify the password
691
+                $this->verifyPassword($share->getPassword());
692
+
693
+                // If a password is set. Hash it!
694
+                if ($share->getShareType() === IShare::TYPE_LINK
695
+                    && $share->getPassword() !== null) {
696
+                    $share->setPassword($this->hasher->hash($share->getPassword()));
697
+                }
698
+            }
699
+
700
+            // Cannot share with the owner
701
+            if ($share->getShareType() === IShare::TYPE_USER
702
+                && $share->getSharedWith() === $share->getShareOwner()) {
703
+                throw new \InvalidArgumentException($this->l->t('Cannot share with the share owner'));
704
+            }
705
+
706
+            // Generate the target
707
+            $shareFolder = $this->config->getSystemValue('share_folder', '/');
708
+            if ($share->getShareType() === IShare::TYPE_USER) {
709
+                $allowCustomShareFolder = $this->config->getSystemValueBool('sharing.allow_custom_share_folder', true);
710
+                if ($allowCustomShareFolder) {
711
+                    $shareFolder = $this->config->getUserValue($share->getSharedWith(), Application::APP_ID, 'share_folder', $shareFolder);
712
+                }
713
+            }
714
+
715
+            $target = $shareFolder . '/' . $share->getNode()->getName();
716
+            $target = \OC\Files\Filesystem::normalizePath($target);
717
+            $share->setTarget($target);
718
+
719
+            // Pre share event
720
+            $event = new Share\Events\BeforeShareCreatedEvent($share);
721
+            $this->dispatchEvent($event, 'before share created');
722
+            if ($event->isPropagationStopped() && $event->getError()) {
723
+                throw new \Exception($event->getError());
724
+            }
725
+
726
+            $oldShare = $share;
727
+            $provider = $this->factory->getProviderForType($share->getShareType());
728
+            $share = $provider->create($share);
729
+
730
+            // Reuse the node we already have
731
+            $share->setNode($oldShare->getNode());
732
+
733
+            // Reset the target if it is null for the new share
734
+            if ($share->getTarget() === '') {
735
+                $share->setTarget($target);
736
+            }
737
+        } catch (AlreadySharedException $e) {
738
+            // If a share for the same target already exists, dont create a new one,
739
+            // but do trigger the hooks and notifications again
740
+            $oldShare = $share;
741
+
742
+            // Reuse the node we already have
743
+            $share = $e->getExistingShare();
744
+            $share->setNode($oldShare->getNode());
745
+        }
746
+
747
+        // Post share event
748
+        $this->dispatchEvent(new ShareCreatedEvent($share), 'share created');
749
+
750
+        // Send email if needed
751
+        if ($this->config->getSystemValueBool('sharing.enable_share_mail', true)) {
752
+            if ($share->getMailSend()) {
753
+                $provider = $this->factory->getProviderForType($share->getShareType());
754
+                if ($provider instanceof IShareProviderWithNotification) {
755
+                    $provider->sendMailNotification($share);
756
+                } else {
757
+                    $this->logger->debug('Share notification not sent because the provider does not support it.', ['app' => 'share']);
758
+                }
759
+            } else {
760
+                $this->logger->debug('Share notification not sent because mailsend is false.', ['app' => 'share']);
761
+            }
762
+        } else {
763
+            $this->logger->debug('Share notification not sent because sharing notification emails is disabled.', ['app' => 'share']);
764
+        }
765
+
766
+        return $share;
767
+    }
768
+
769
+    /**
770
+     * Update a share
771
+     *
772
+     * @param IShare $share
773
+     * @return IShare The share object
774
+     * @throws \InvalidArgumentException
775
+     * @throws HintException
776
+     */
777
+    public function updateShare(IShare $share, bool $onlyValid = true) {
778
+        $expirationDateUpdated = false;
779
+
780
+        $this->canShare($share);
781
+
782
+        try {
783
+            $originalShare = $this->getShareById($share->getFullId(), onlyValid: $onlyValid);
784
+        } catch (\UnexpectedValueException $e) {
785
+            throw new \InvalidArgumentException($this->l->t('Share does not have a full ID'));
786
+        }
787
+
788
+        // We cannot change the share type!
789
+        if ($share->getShareType() !== $originalShare->getShareType()) {
790
+            throw new \InvalidArgumentException($this->l->t('Cannot change share type'));
791
+        }
792
+
793
+        // We can only change the recipient on user shares
794
+        if ($share->getSharedWith() !== $originalShare->getSharedWith()
795
+            && $share->getShareType() !== IShare::TYPE_USER) {
796
+            throw new \InvalidArgumentException($this->l->t('Can only update recipient on user shares'));
797
+        }
798
+
799
+        // Cannot share with the owner
800
+        if ($share->getShareType() === IShare::TYPE_USER
801
+            && $share->getSharedWith() === $share->getShareOwner()) {
802
+            throw new \InvalidArgumentException($this->l->t('Cannot share with the share owner'));
803
+        }
804
+
805
+        $this->generalCreateChecks($share, true);
806
+
807
+        if ($share->getShareType() === IShare::TYPE_USER) {
808
+            $this->userCreateChecks($share);
809
+
810
+            if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
811
+                // Verify the expiration date
812
+                $this->validateExpirationDateInternal($share);
813
+                $expirationDateUpdated = true;
814
+            }
815
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
816
+            $this->groupCreateChecks($share);
817
+
818
+            if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
819
+                // Verify the expiration date
820
+                $this->validateExpirationDateInternal($share);
821
+                $expirationDateUpdated = true;
822
+            }
823
+        } elseif ($share->getShareType() === IShare::TYPE_LINK
824
+            || $share->getShareType() === IShare::TYPE_EMAIL) {
825
+            $this->linkCreateChecks($share);
826
+
827
+            // The new password is not set again if it is the same as the old
828
+            // one, unless when switching from sending by Talk to sending by
829
+            // mail.
830
+            $plainTextPassword = $share->getPassword();
831
+            $updatedPassword = $this->updateSharePasswordIfNeeded($share, $originalShare);
832
+
833
+            /**
834
+             * Cannot enable the getSendPasswordByTalk if there is no password set
835
+             */
836
+            if (empty($plainTextPassword) && $share->getSendPasswordByTalk()) {
837
+                throw new \InvalidArgumentException($this->l->t('Cannot enable sending the password by Talk with an empty password'));
838
+            }
839
+
840
+            /**
841
+             * If we're in a mail share, we need to force a password change
842
+             * as either the user is not aware of the password or is already (received by mail)
843
+             * Thus the SendPasswordByTalk feature would not make sense
844
+             */
845
+            if (!$updatedPassword && $share->getShareType() === IShare::TYPE_EMAIL) {
846
+                if (!$originalShare->getSendPasswordByTalk() && $share->getSendPasswordByTalk()) {
847
+                    throw new \InvalidArgumentException($this->l->t('Cannot enable sending the password by Talk without setting a new password'));
848
+                }
849
+                if ($originalShare->getSendPasswordByTalk() && !$share->getSendPasswordByTalk()) {
850
+                    throw new \InvalidArgumentException($this->l->t('Cannot disable sending the password by Talk without setting a new password'));
851
+                }
852
+            }
853
+
854
+            if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
855
+                // Verify the expiration date
856
+                $this->validateExpirationDateLink($share);
857
+                $expirationDateUpdated = true;
858
+            }
859
+        } elseif ($share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
860
+            if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
861
+                // Verify the expiration date
862
+                $this->validateExpirationDateInternal($share);
863
+                $expirationDateUpdated = true;
864
+            }
865
+        }
866
+
867
+        $this->pathCreateChecks($share->getNode());
868
+
869
+        // Now update the share!
870
+        $provider = $this->factory->getProviderForType($share->getShareType());
871
+        if ($share->getShareType() === IShare::TYPE_EMAIL) {
872
+            /** @var ShareByMailProvider $provider */
873
+            $share = $provider->update($share, $plainTextPassword);
874
+        } else {
875
+            $share = $provider->update($share);
876
+        }
877
+
878
+        if ($expirationDateUpdated === true) {
879
+            \OC_Hook::emit(Share::class, 'post_set_expiration_date', [
880
+                'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
881
+                'itemSource' => $share->getNode()->getId(),
882
+                'date' => $share->getExpirationDate(),
883
+                'uidOwner' => $share->getSharedBy(),
884
+            ]);
885
+        }
886
+
887
+        if ($share->getPassword() !== $originalShare->getPassword()) {
888
+            \OC_Hook::emit(Share::class, 'post_update_password', [
889
+                'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
890
+                'itemSource' => $share->getNode()->getId(),
891
+                'uidOwner' => $share->getSharedBy(),
892
+                'token' => $share->getToken(),
893
+                'disabled' => is_null($share->getPassword()),
894
+            ]);
895
+        }
896
+
897
+        if ($share->getPermissions() !== $originalShare->getPermissions()) {
898
+            if ($this->userManager->userExists($share->getShareOwner())) {
899
+                $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
900
+            } else {
901
+                $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
902
+            }
903
+            \OC_Hook::emit(Share::class, 'post_update_permissions', [
904
+                'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
905
+                'itemSource' => $share->getNode()->getId(),
906
+                'shareType' => $share->getShareType(),
907
+                'shareWith' => $share->getSharedWith(),
908
+                'uidOwner' => $share->getSharedBy(),
909
+                'permissions' => $share->getPermissions(),
910
+                'attributes' => $share->getAttributes() !== null ? $share->getAttributes()->toArray() : null,
911
+                'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
912
+            ]);
913
+        }
914
+
915
+        return $share;
916
+    }
917
+
918
+    /**
919
+     * Accept a share.
920
+     *
921
+     * @param IShare $share
922
+     * @param string $recipientId
923
+     * @return IShare The share object
924
+     * @throws \InvalidArgumentException Thrown if the provider does not implement `IShareProviderSupportsAccept`
925
+     * @since 9.0.0
926
+     */
927
+    public function acceptShare(IShare $share, string $recipientId): IShare {
928
+        [$providerId,] = $this->splitFullId($share->getFullId());
929
+        $provider = $this->factory->getProvider($providerId);
930
+
931
+        if (!($provider instanceof IShareProviderSupportsAccept)) {
932
+            throw new \InvalidArgumentException($this->l->t('Share provider does not support accepting'));
933
+        }
934
+        /** @var IShareProvider&IShareProviderSupportsAccept $provider */
935
+        $provider->acceptShare($share, $recipientId);
936
+
937
+        $event = new ShareAcceptedEvent($share);
938
+        $this->dispatchEvent($event, 'share accepted');
939
+
940
+        return $share;
941
+    }
942
+
943
+    /**
944
+     * Updates the password of the given share if it is not the same as the
945
+     * password of the original share.
946
+     *
947
+     * @param IShare $share the share to update its password.
948
+     * @param IShare $originalShare the original share to compare its
949
+     *                              password with.
950
+     * @return boolean whether the password was updated or not.
951
+     */
952
+    private function updateSharePasswordIfNeeded(IShare $share, IShare $originalShare) {
953
+        $passwordsAreDifferent = ($share->getPassword() !== $originalShare->getPassword())
954
+            && (($share->getPassword() !== null && $originalShare->getPassword() === null)
955
+                || ($share->getPassword() === null && $originalShare->getPassword() !== null)
956
+                || ($share->getPassword() !== null && $originalShare->getPassword() !== null
957
+                    && !$this->hasher->verify($share->getPassword(), $originalShare->getPassword())));
958
+
959
+        // Password updated.
960
+        if ($passwordsAreDifferent) {
961
+            // Verify the password
962
+            $this->verifyPassword($share->getPassword());
963
+
964
+            // If a password is set. Hash it!
965
+            if (!empty($share->getPassword())) {
966
+                $share->setPassword($this->hasher->hash($share->getPassword()));
967
+                if ($share->getShareType() === IShare::TYPE_EMAIL) {
968
+                    // Shares shared by email have temporary passwords
969
+                    $this->setSharePasswordExpirationTime($share);
970
+                }
971
+
972
+                return true;
973
+            } else {
974
+                // Empty string and null are seen as NOT password protected
975
+                $share->setPassword(null);
976
+                if ($share->getShareType() === IShare::TYPE_EMAIL) {
977
+                    $share->setPasswordExpirationTime(null);
978
+                }
979
+                return true;
980
+            }
981
+        } else {
982
+            // Reset the password to the original one, as it is either the same
983
+            // as the "new" password or a hashed version of it.
984
+            $share->setPassword($originalShare->getPassword());
985
+        }
986
+
987
+        return false;
988
+    }
989
+
990
+    /**
991
+     * Set the share's password expiration time
992
+     */
993
+    private function setSharePasswordExpirationTime(IShare $share): void {
994
+        if (!$this->config->getSystemValueBool('sharing.enable_mail_link_password_expiration', false)) {
995
+            // Sets password expiration date to NULL
996
+            $share->setPasswordExpirationTime();
997
+            return;
998
+        }
999
+        // Sets password expiration date
1000
+        $expirationTime = null;
1001
+        $now = new \DateTime();
1002
+        $expirationInterval = $this->config->getSystemValue('sharing.mail_link_password_expiration_interval', 3600);
1003
+        $expirationTime = $now->add(new \DateInterval('PT' . $expirationInterval . 'S'));
1004
+        $share->setPasswordExpirationTime($expirationTime);
1005
+    }
1006
+
1007
+
1008
+    /**
1009
+     * Delete all the children of this share
1010
+     *
1011
+     * @param IShare $share
1012
+     * @return IShare[] List of deleted shares
1013
+     */
1014
+    protected function deleteChildren(IShare $share) {
1015
+        $deletedShares = [];
1016
+
1017
+        $provider = $this->factory->getProviderForType($share->getShareType());
1018
+
1019
+        foreach ($provider->getChildren($share) as $child) {
1020
+            $this->dispatchEvent(new BeforeShareDeletedEvent($child), 'before share deleted');
1021
+
1022
+            $deletedChildren = $this->deleteChildren($child);
1023
+            $deletedShares = array_merge($deletedShares, $deletedChildren);
1024
+
1025
+            $provider->delete($child);
1026
+            $this->dispatchEvent(new ShareDeletedEvent($child), 'share deleted');
1027
+            $deletedShares[] = $child;
1028
+        }
1029
+
1030
+        return $deletedShares;
1031
+    }
1032
+
1033
+    /** Promote re-shares into direct shares so that target user keeps access */
1034
+    protected function promoteReshares(IShare $share): void {
1035
+        try {
1036
+            $node = $share->getNode();
1037
+        } catch (NotFoundException) {
1038
+            /* Skip if node not found */
1039
+            return;
1040
+        }
1041
+
1042
+        $userIds = [];
1043
+
1044
+        if ($share->getShareType() === IShare::TYPE_USER) {
1045
+            $userIds[] = $share->getSharedWith();
1046
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
1047
+            $group = $this->groupManager->get($share->getSharedWith());
1048
+            $users = $group?->getUsers() ?? [];
1049
+
1050
+            foreach ($users as $user) {
1051
+                /* Skip share owner */
1052
+                if ($user->getUID() === $share->getShareOwner() || $user->getUID() === $share->getSharedBy()) {
1053
+                    continue;
1054
+                }
1055
+                $userIds[] = $user->getUID();
1056
+            }
1057
+        } else {
1058
+            /* We only support user and group shares */
1059
+            return;
1060
+        }
1061
+
1062
+        $reshareRecords = [];
1063
+        $shareTypes = [
1064
+            IShare::TYPE_GROUP,
1065
+            IShare::TYPE_USER,
1066
+            IShare::TYPE_LINK,
1067
+            IShare::TYPE_REMOTE,
1068
+            IShare::TYPE_EMAIL,
1069
+        ];
1070
+
1071
+        foreach ($userIds as $userId) {
1072
+            foreach ($shareTypes as $shareType) {
1073
+                try {
1074
+                    $provider = $this->factory->getProviderForType($shareType);
1075
+                } catch (ProviderException $e) {
1076
+                    continue;
1077
+                }
1078
+
1079
+                if ($node instanceof Folder) {
1080
+                    /* We need to get all shares by this user to get subshares */
1081
+                    $shares = $provider->getSharesBy($userId, $shareType, null, false, -1, 0);
1082
+
1083
+                    foreach ($shares as $share) {
1084
+                        try {
1085
+                            $path = $share->getNode()->getPath();
1086
+                        } catch (NotFoundException) {
1087
+                            /* Ignore share of non-existing node */
1088
+                            continue;
1089
+                        }
1090
+                        if ($node->getRelativePath($path) !== null) {
1091
+                            /* If relative path is not null it means the shared node is the same or in a subfolder */
1092
+                            $reshareRecords[] = $share;
1093
+                        }
1094
+                    }
1095
+                } else {
1096
+                    $shares = $provider->getSharesBy($userId, $shareType, $node, false, -1, 0);
1097
+                    foreach ($shares as $child) {
1098
+                        $reshareRecords[] = $child;
1099
+                    }
1100
+                }
1101
+            }
1102
+        }
1103
+
1104
+        foreach ($reshareRecords as $child) {
1105
+            try {
1106
+                /* Check if the share is still valid (means the resharer still has access to the file through another mean) */
1107
+                $this->generalCreateChecks($child);
1108
+            } catch (GenericShareException $e) {
1109
+                /* The check is invalid, promote it to a direct share from the sharer of parent share */
1110
+                $this->logger->debug('Promote reshare because of exception ' . $e->getMessage(), ['exception' => $e, 'fullId' => $child->getFullId()]);
1111
+                try {
1112
+                    $child->setSharedBy($share->getSharedBy());
1113
+                    $this->updateShare($child);
1114
+                } catch (GenericShareException|\InvalidArgumentException $e) {
1115
+                    $this->logger->warning('Failed to promote reshare because of exception ' . $e->getMessage(), ['exception' => $e, 'fullId' => $child->getFullId()]);
1116
+                }
1117
+            }
1118
+        }
1119
+    }
1120
+
1121
+    /**
1122
+     * Delete a share
1123
+     *
1124
+     * @param IShare $share
1125
+     * @throws ShareNotFound
1126
+     * @throws \InvalidArgumentException
1127
+     */
1128
+    public function deleteShare(IShare $share) {
1129
+        try {
1130
+            $share->getFullId();
1131
+        } catch (\UnexpectedValueException $e) {
1132
+            throw new \InvalidArgumentException($this->l->t('Share does not have a full ID'));
1133
+        }
1134
+
1135
+        $this->dispatchEvent(new BeforeShareDeletedEvent($share), 'before share deleted');
1136
+
1137
+        // Get all children and delete them as well
1138
+        $this->deleteChildren($share);
1139
+
1140
+        // Do the actual delete
1141
+        $provider = $this->factory->getProviderForType($share->getShareType());
1142
+        $provider->delete($share);
1143
+
1144
+        $this->dispatchEvent(new ShareDeletedEvent($share), 'share deleted');
1145
+
1146
+        // Promote reshares of the deleted share
1147
+        $this->promoteReshares($share);
1148
+    }
1149
+
1150
+
1151
+    /**
1152
+     * Unshare a file as the recipient.
1153
+     * This can be different from a regular delete for example when one of
1154
+     * the users in a groups deletes that share. But the provider should
1155
+     * handle this.
1156
+     *
1157
+     * @param IShare $share
1158
+     * @param string $recipientId
1159
+     */
1160
+    public function deleteFromSelf(IShare $share, $recipientId) {
1161
+        [$providerId,] = $this->splitFullId($share->getFullId());
1162
+        $provider = $this->factory->getProvider($providerId);
1163
+
1164
+        $provider->deleteFromSelf($share, $recipientId);
1165
+        $event = new ShareDeletedFromSelfEvent($share);
1166
+        $this->dispatchEvent($event, 'leave share');
1167
+    }
1168
+
1169
+    public function restoreShare(IShare $share, string $recipientId): IShare {
1170
+        [$providerId,] = $this->splitFullId($share->getFullId());
1171
+        $provider = $this->factory->getProvider($providerId);
1172
+
1173
+        return $provider->restore($share, $recipientId);
1174
+    }
1175
+
1176
+    /**
1177
+     * @inheritdoc
1178
+     */
1179
+    public function moveShare(IShare $share, $recipientId) {
1180
+        if ($share->getShareType() === IShare::TYPE_LINK
1181
+            || $share->getShareType() === IShare::TYPE_EMAIL) {
1182
+            throw new \InvalidArgumentException($this->l->t('Cannot change target of link share'));
1183
+        }
1184
+
1185
+        if ($share->getShareType() === IShare::TYPE_USER && $share->getSharedWith() !== $recipientId) {
1186
+            throw new \InvalidArgumentException($this->l->t('Invalid share recipient'));
1187
+        }
1188
+
1189
+        if ($share->getShareType() === IShare::TYPE_GROUP) {
1190
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
1191
+            if (is_null($sharedWith)) {
1192
+                throw new \InvalidArgumentException($this->l->t('Group "%s" does not exist', [$share->getSharedWith()]));
1193
+            }
1194
+            $recipient = $this->userManager->get($recipientId);
1195
+            if (!$sharedWith->inGroup($recipient)) {
1196
+                throw new \InvalidArgumentException($this->l->t('Invalid share recipient'));
1197
+            }
1198
+        }
1199
+
1200
+        [$providerId,] = $this->splitFullId($share->getFullId());
1201
+        $provider = $this->factory->getProvider($providerId);
1202
+
1203
+        return $provider->move($share, $recipientId);
1204
+    }
1205
+
1206
+    public function getSharesInFolder($userId, Folder $node, $reshares = false, $shallow = true) {
1207
+        $providers = $this->factory->getAllProviders();
1208
+        if (!$shallow) {
1209
+            throw new \Exception('non-shallow getSharesInFolder is no longer supported');
1210
+        }
1211
+
1212
+        $isOwnerless = $node->getMountPoint() instanceof IShareOwnerlessMount;
1213
+
1214
+        $shares = [];
1215
+        foreach ($providers as $provider) {
1216
+            if ($isOwnerless) {
1217
+                // If the provider does not implement the additional interface,
1218
+                // we lack a performant way of querying all shares and therefore ignore the provider.
1219
+                if ($provider instanceof IShareProviderSupportsAllSharesInFolder) {
1220
+                    foreach ($provider->getAllSharesInFolder($node) as $fid => $data) {
1221
+                        $shares[$fid] ??= [];
1222
+                        $shares[$fid] = array_merge($shares[$fid], $data);
1223
+                    }
1224
+                }
1225
+            } else {
1226
+                foreach ($provider->getSharesInFolder($userId, $node, $reshares) as $fid => $data) {
1227
+                    $shares[$fid] ??= [];
1228
+                    $shares[$fid] = array_merge($shares[$fid], $data);
1229
+                }
1230
+            }
1231
+        }
1232
+
1233
+        return $shares;
1234
+    }
1235
+
1236
+    /**
1237
+     * @inheritdoc
1238
+     */
1239
+    public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0, bool $onlyValid = true) {
1240
+        if ($path !== null
1241
+            && !($path instanceof \OCP\Files\File)
1242
+            && !($path instanceof \OCP\Files\Folder)) {
1243
+            throw new \InvalidArgumentException($this->l->t('Invalid path'));
1244
+        }
1245
+
1246
+        try {
1247
+            $provider = $this->factory->getProviderForType($shareType);
1248
+        } catch (ProviderException $e) {
1249
+            return [];
1250
+        }
1251
+
1252
+        if ($path?->getMountPoint() instanceof IShareOwnerlessMount) {
1253
+            $shares = array_filter($provider->getSharesByPath($path), static fn (IShare $share) => $share->getShareType() === $shareType);
1254
+        } else {
1255
+            $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1256
+        }
1257
+
1258
+        /*
1259 1259
 		 * Work around so we don't return expired shares but still follow
1260 1260
 		 * proper pagination.
1261 1261
 		 */
1262 1262
 
1263
-		$shares2 = [];
1264
-
1265
-		while (true) {
1266
-			$added = 0;
1267
-			foreach ($shares as $share) {
1268
-				if ($onlyValid) {
1269
-					try {
1270
-						$this->checkShare($share);
1271
-					} catch (ShareNotFound $e) {
1272
-						// Ignore since this basically means the share is deleted
1273
-						continue;
1274
-					}
1275
-				}
1276
-
1277
-				$added++;
1278
-				$shares2[] = $share;
1279
-
1280
-				if (count($shares2) === $limit) {
1281
-					break;
1282
-				}
1283
-			}
1284
-
1285
-			// If we did not fetch more shares than the limit then there are no more shares
1286
-			if (count($shares) < $limit) {
1287
-				break;
1288
-			}
1289
-
1290
-			if (count($shares2) === $limit) {
1291
-				break;
1292
-			}
1293
-
1294
-			// If there was no limit on the select we are done
1295
-			if ($limit === -1) {
1296
-				break;
1297
-			}
1298
-
1299
-			$offset += $added;
1300
-
1301
-			// Fetch again $limit shares
1302
-			if ($path?->getMountPoint() instanceof IShareOwnerlessMount) {
1303
-				// We already fetched all shares, so end here
1304
-				$shares = [];
1305
-			} else {
1306
-				$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1307
-			}
1308
-
1309
-			// No more shares means we are done
1310
-			if (empty($shares)) {
1311
-				break;
1312
-			}
1313
-		}
1314
-
1315
-		$shares = $shares2;
1316
-
1317
-		return $shares;
1318
-	}
1319
-
1320
-	/**
1321
-	 * @inheritdoc
1322
-	 */
1323
-	public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1324
-		try {
1325
-			$provider = $this->factory->getProviderForType($shareType);
1326
-		} catch (ProviderException $e) {
1327
-			return [];
1328
-		}
1329
-
1330
-		$shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
1331
-
1332
-		// remove all shares which are already expired
1333
-		foreach ($shares as $key => $share) {
1334
-			try {
1335
-				$this->checkShare($share);
1336
-			} catch (ShareNotFound $e) {
1337
-				unset($shares[$key]);
1338
-			}
1339
-		}
1340
-
1341
-		return $shares;
1342
-	}
1343
-
1344
-	/**
1345
-	 * @inheritdoc
1346
-	 */
1347
-	public function getDeletedSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1348
-		$shares = $this->getSharedWith($userId, $shareType, $node, $limit, $offset);
1349
-
1350
-		// Only get deleted shares
1351
-		$shares = array_filter($shares, function (IShare $share) {
1352
-			return $share->getPermissions() === 0;
1353
-		});
1354
-
1355
-		// Only get shares where the owner still exists
1356
-		$shares = array_filter($shares, function (IShare $share) {
1357
-			return $this->userManager->userExists($share->getShareOwner());
1358
-		});
1359
-
1360
-		return $shares;
1361
-	}
1362
-
1363
-	/**
1364
-	 * @inheritdoc
1365
-	 */
1366
-	public function getShareById($id, $recipient = null, bool $onlyValid = true) {
1367
-		if ($id === null) {
1368
-			throw new ShareNotFound();
1369
-		}
1370
-
1371
-		[$providerId, $id] = $this->splitFullId($id);
1372
-
1373
-		try {
1374
-			$provider = $this->factory->getProvider($providerId);
1375
-		} catch (ProviderException $e) {
1376
-			throw new ShareNotFound();
1377
-		}
1378
-
1379
-		$share = $provider->getShareById($id, $recipient);
1380
-
1381
-		if ($onlyValid) {
1382
-			$this->checkShare($share);
1383
-		}
1384
-
1385
-		return $share;
1386
-	}
1387
-
1388
-	/**
1389
-	 * Get all the shares for a given path
1390
-	 *
1391
-	 * @param \OCP\Files\Node $path
1392
-	 * @param int $page
1393
-	 * @param int $perPage
1394
-	 *
1395
-	 * @return Share[]
1396
-	 */
1397
-	public function getSharesByPath(\OCP\Files\Node $path, $page = 0, $perPage = 50) {
1398
-		return [];
1399
-	}
1400
-
1401
-	/**
1402
-	 * Get the share by token possible with password
1403
-	 *
1404
-	 * @param string $token
1405
-	 * @return IShare
1406
-	 *
1407
-	 * @throws ShareNotFound
1408
-	 */
1409
-	public function getShareByToken($token) {
1410
-		// tokens cannot be valid local user names
1411
-		if ($this->userManager->userExists($token)) {
1412
-			throw new ShareNotFound();
1413
-		}
1414
-		$share = null;
1415
-		try {
1416
-			if ($this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes') {
1417
-				$provider = $this->factory->getProviderForType(IShare::TYPE_LINK);
1418
-				$share = $provider->getShareByToken($token);
1419
-			}
1420
-		} catch (ProviderException $e) {
1421
-		} catch (ShareNotFound $e) {
1422
-		}
1423
-
1424
-
1425
-		// If it is not a link share try to fetch a federated share by token
1426
-		if ($share === null) {
1427
-			try {
1428
-				$provider = $this->factory->getProviderForType(IShare::TYPE_REMOTE);
1429
-				$share = $provider->getShareByToken($token);
1430
-			} catch (ProviderException $e) {
1431
-			} catch (ShareNotFound $e) {
1432
-			}
1433
-		}
1434
-
1435
-		// If it is not a link share try to fetch a mail share by token
1436
-		if ($share === null && $this->shareProviderExists(IShare::TYPE_EMAIL)) {
1437
-			try {
1438
-				$provider = $this->factory->getProviderForType(IShare::TYPE_EMAIL);
1439
-				$share = $provider->getShareByToken($token);
1440
-			} catch (ProviderException $e) {
1441
-			} catch (ShareNotFound $e) {
1442
-			}
1443
-		}
1444
-
1445
-		if ($share === null && $this->shareProviderExists(IShare::TYPE_CIRCLE)) {
1446
-			try {
1447
-				$provider = $this->factory->getProviderForType(IShare::TYPE_CIRCLE);
1448
-				$share = $provider->getShareByToken($token);
1449
-			} catch (ProviderException $e) {
1450
-			} catch (ShareNotFound $e) {
1451
-			}
1452
-		}
1453
-
1454
-		if ($share === null && $this->shareProviderExists(IShare::TYPE_ROOM)) {
1455
-			try {
1456
-				$provider = $this->factory->getProviderForType(IShare::TYPE_ROOM);
1457
-				$share = $provider->getShareByToken($token);
1458
-			} catch (ProviderException $e) {
1459
-			} catch (ShareNotFound $e) {
1460
-			}
1461
-		}
1462
-
1463
-		if ($share === null) {
1464
-			throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1465
-		}
1466
-
1467
-		$this->checkShare($share);
1468
-
1469
-		/*
1263
+        $shares2 = [];
1264
+
1265
+        while (true) {
1266
+            $added = 0;
1267
+            foreach ($shares as $share) {
1268
+                if ($onlyValid) {
1269
+                    try {
1270
+                        $this->checkShare($share);
1271
+                    } catch (ShareNotFound $e) {
1272
+                        // Ignore since this basically means the share is deleted
1273
+                        continue;
1274
+                    }
1275
+                }
1276
+
1277
+                $added++;
1278
+                $shares2[] = $share;
1279
+
1280
+                if (count($shares2) === $limit) {
1281
+                    break;
1282
+                }
1283
+            }
1284
+
1285
+            // If we did not fetch more shares than the limit then there are no more shares
1286
+            if (count($shares) < $limit) {
1287
+                break;
1288
+            }
1289
+
1290
+            if (count($shares2) === $limit) {
1291
+                break;
1292
+            }
1293
+
1294
+            // If there was no limit on the select we are done
1295
+            if ($limit === -1) {
1296
+                break;
1297
+            }
1298
+
1299
+            $offset += $added;
1300
+
1301
+            // Fetch again $limit shares
1302
+            if ($path?->getMountPoint() instanceof IShareOwnerlessMount) {
1303
+                // We already fetched all shares, so end here
1304
+                $shares = [];
1305
+            } else {
1306
+                $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1307
+            }
1308
+
1309
+            // No more shares means we are done
1310
+            if (empty($shares)) {
1311
+                break;
1312
+            }
1313
+        }
1314
+
1315
+        $shares = $shares2;
1316
+
1317
+        return $shares;
1318
+    }
1319
+
1320
+    /**
1321
+     * @inheritdoc
1322
+     */
1323
+    public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1324
+        try {
1325
+            $provider = $this->factory->getProviderForType($shareType);
1326
+        } catch (ProviderException $e) {
1327
+            return [];
1328
+        }
1329
+
1330
+        $shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
1331
+
1332
+        // remove all shares which are already expired
1333
+        foreach ($shares as $key => $share) {
1334
+            try {
1335
+                $this->checkShare($share);
1336
+            } catch (ShareNotFound $e) {
1337
+                unset($shares[$key]);
1338
+            }
1339
+        }
1340
+
1341
+        return $shares;
1342
+    }
1343
+
1344
+    /**
1345
+     * @inheritdoc
1346
+     */
1347
+    public function getDeletedSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1348
+        $shares = $this->getSharedWith($userId, $shareType, $node, $limit, $offset);
1349
+
1350
+        // Only get deleted shares
1351
+        $shares = array_filter($shares, function (IShare $share) {
1352
+            return $share->getPermissions() === 0;
1353
+        });
1354
+
1355
+        // Only get shares where the owner still exists
1356
+        $shares = array_filter($shares, function (IShare $share) {
1357
+            return $this->userManager->userExists($share->getShareOwner());
1358
+        });
1359
+
1360
+        return $shares;
1361
+    }
1362
+
1363
+    /**
1364
+     * @inheritdoc
1365
+     */
1366
+    public function getShareById($id, $recipient = null, bool $onlyValid = true) {
1367
+        if ($id === null) {
1368
+            throw new ShareNotFound();
1369
+        }
1370
+
1371
+        [$providerId, $id] = $this->splitFullId($id);
1372
+
1373
+        try {
1374
+            $provider = $this->factory->getProvider($providerId);
1375
+        } catch (ProviderException $e) {
1376
+            throw new ShareNotFound();
1377
+        }
1378
+
1379
+        $share = $provider->getShareById($id, $recipient);
1380
+
1381
+        if ($onlyValid) {
1382
+            $this->checkShare($share);
1383
+        }
1384
+
1385
+        return $share;
1386
+    }
1387
+
1388
+    /**
1389
+     * Get all the shares for a given path
1390
+     *
1391
+     * @param \OCP\Files\Node $path
1392
+     * @param int $page
1393
+     * @param int $perPage
1394
+     *
1395
+     * @return Share[]
1396
+     */
1397
+    public function getSharesByPath(\OCP\Files\Node $path, $page = 0, $perPage = 50) {
1398
+        return [];
1399
+    }
1400
+
1401
+    /**
1402
+     * Get the share by token possible with password
1403
+     *
1404
+     * @param string $token
1405
+     * @return IShare
1406
+     *
1407
+     * @throws ShareNotFound
1408
+     */
1409
+    public function getShareByToken($token) {
1410
+        // tokens cannot be valid local user names
1411
+        if ($this->userManager->userExists($token)) {
1412
+            throw new ShareNotFound();
1413
+        }
1414
+        $share = null;
1415
+        try {
1416
+            if ($this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes') {
1417
+                $provider = $this->factory->getProviderForType(IShare::TYPE_LINK);
1418
+                $share = $provider->getShareByToken($token);
1419
+            }
1420
+        } catch (ProviderException $e) {
1421
+        } catch (ShareNotFound $e) {
1422
+        }
1423
+
1424
+
1425
+        // If it is not a link share try to fetch a federated share by token
1426
+        if ($share === null) {
1427
+            try {
1428
+                $provider = $this->factory->getProviderForType(IShare::TYPE_REMOTE);
1429
+                $share = $provider->getShareByToken($token);
1430
+            } catch (ProviderException $e) {
1431
+            } catch (ShareNotFound $e) {
1432
+            }
1433
+        }
1434
+
1435
+        // If it is not a link share try to fetch a mail share by token
1436
+        if ($share === null && $this->shareProviderExists(IShare::TYPE_EMAIL)) {
1437
+            try {
1438
+                $provider = $this->factory->getProviderForType(IShare::TYPE_EMAIL);
1439
+                $share = $provider->getShareByToken($token);
1440
+            } catch (ProviderException $e) {
1441
+            } catch (ShareNotFound $e) {
1442
+            }
1443
+        }
1444
+
1445
+        if ($share === null && $this->shareProviderExists(IShare::TYPE_CIRCLE)) {
1446
+            try {
1447
+                $provider = $this->factory->getProviderForType(IShare::TYPE_CIRCLE);
1448
+                $share = $provider->getShareByToken($token);
1449
+            } catch (ProviderException $e) {
1450
+            } catch (ShareNotFound $e) {
1451
+            }
1452
+        }
1453
+
1454
+        if ($share === null && $this->shareProviderExists(IShare::TYPE_ROOM)) {
1455
+            try {
1456
+                $provider = $this->factory->getProviderForType(IShare::TYPE_ROOM);
1457
+                $share = $provider->getShareByToken($token);
1458
+            } catch (ProviderException $e) {
1459
+            } catch (ShareNotFound $e) {
1460
+            }
1461
+        }
1462
+
1463
+        if ($share === null) {
1464
+            throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1465
+        }
1466
+
1467
+        $this->checkShare($share);
1468
+
1469
+        /*
1470 1470
 		 * Reduce the permissions for link or email shares if public upload is not enabled
1471 1471
 		 */
1472
-		if (($share->getShareType() === IShare::TYPE_LINK || $share->getShareType() === IShare::TYPE_EMAIL)
1473
-			&& $share->getNodeType() === 'folder' && !$this->shareApiLinkAllowPublicUpload()) {
1474
-			$share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
1475
-		}
1476
-
1477
-		return $share;
1478
-	}
1479
-
1480
-	/**
1481
-	 * Check expire date and disabled owner
1482
-	 *
1483
-	 * @throws ShareNotFound
1484
-	 */
1485
-	protected function checkShare(IShare $share): void {
1486
-		if ($share->isExpired()) {
1487
-			$this->deleteShare($share);
1488
-			throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1489
-		}
1490
-		if ($this->config->getAppValue('files_sharing', 'hide_disabled_user_shares', 'no') === 'yes') {
1491
-			$uids = array_unique([$share->getShareOwner(),$share->getSharedBy()]);
1492
-			foreach ($uids as $uid) {
1493
-				$user = $this->userManager->get($uid);
1494
-				if ($user?->isEnabled() === false) {
1495
-					throw new ShareNotFound($this->l->t('The requested share comes from a disabled user'));
1496
-				}
1497
-			}
1498
-		}
1499
-
1500
-		// For link and email shares, verify the share owner can still create such shares
1501
-		if ($share->getShareType() === IShare::TYPE_LINK || $share->getShareType() === IShare::TYPE_EMAIL) {
1502
-			$shareOwner = $this->userManager->get($share->getShareOwner());
1503
-			if ($shareOwner === null) {
1504
-				throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1505
-			}
1506
-			if (!$this->userCanCreateLinkShares($shareOwner)) {
1507
-				throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1508
-			}
1509
-		}
1510
-	}
1511
-
1512
-	/**
1513
-	 * Verify the password of a public share
1514
-	 *
1515
-	 * @param IShare $share
1516
-	 * @param ?string $password
1517
-	 * @return bool
1518
-	 */
1519
-	public function checkPassword(IShare $share, $password) {
1520
-
1521
-		// if there is no password on the share object / passsword is null, there is nothing to check
1522
-		if ($password === null || $share->getPassword() === null) {
1523
-			return false;
1524
-		}
1525
-
1526
-		// Makes sure password hasn't expired
1527
-		$expirationTime = $share->getPasswordExpirationTime();
1528
-		if ($expirationTime !== null && $expirationTime < new \DateTime()) {
1529
-			return false;
1530
-		}
1531
-
1532
-		$newHash = '';
1533
-		if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
1534
-			return false;
1535
-		}
1536
-
1537
-		if (!empty($newHash)) {
1538
-			$share->setPassword($newHash);
1539
-			$provider = $this->factory->getProviderForType($share->getShareType());
1540
-			$provider->update($share);
1541
-		}
1542
-
1543
-		return true;
1544
-	}
1545
-
1546
-	/**
1547
-	 * @inheritdoc
1548
-	 */
1549
-	public function userDeleted($uid) {
1550
-		$types = [IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK, IShare::TYPE_REMOTE, IShare::TYPE_EMAIL];
1551
-
1552
-		foreach ($types as $type) {
1553
-			try {
1554
-				$provider = $this->factory->getProviderForType($type);
1555
-			} catch (ProviderException $e) {
1556
-				continue;
1557
-			}
1558
-			$provider->userDeleted($uid, $type);
1559
-		}
1560
-	}
1561
-
1562
-	/**
1563
-	 * @inheritdoc
1564
-	 */
1565
-	public function groupDeleted($gid) {
1566
-		foreach ([IShare::TYPE_GROUP, IShare::TYPE_REMOTE_GROUP] as $type) {
1567
-			try {
1568
-				$provider = $this->factory->getProviderForType($type);
1569
-			} catch (ProviderException $e) {
1570
-				continue;
1571
-			}
1572
-			$provider->groupDeleted($gid);
1573
-		}
1574
-
1575
-		$excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1576
-		if ($excludedGroups === '') {
1577
-			return;
1578
-		}
1579
-
1580
-		$excludedGroups = json_decode($excludedGroups, true);
1581
-		if (json_last_error() !== JSON_ERROR_NONE) {
1582
-			return;
1583
-		}
1584
-
1585
-		$excludedGroups = array_diff($excludedGroups, [$gid]);
1586
-		$this->config->setAppValue('core', 'shareapi_exclude_groups_list', json_encode($excludedGroups));
1587
-	}
1588
-
1589
-	/**
1590
-	 * @inheritdoc
1591
-	 */
1592
-	public function userDeletedFromGroup($uid, $gid) {
1593
-		foreach ([IShare::TYPE_GROUP, IShare::TYPE_REMOTE_GROUP] as $type) {
1594
-			try {
1595
-				$provider = $this->factory->getProviderForType($type);
1596
-			} catch (ProviderException $e) {
1597
-				continue;
1598
-			}
1599
-			$provider->userDeletedFromGroup($uid, $gid);
1600
-		}
1601
-	}
1602
-
1603
-	/**
1604
-	 * Get access list to a path. This means
1605
-	 * all the users that can access a given path.
1606
-	 *
1607
-	 * Consider:
1608
-	 * -root
1609
-	 * |-folder1 (23)
1610
-	 *  |-folder2 (32)
1611
-	 *   |-fileA (42)
1612
-	 *
1613
-	 * fileA is shared with user1 and user1@server1 and email1@maildomain1
1614
-	 * folder2 is shared with group2 (user4 is a member of group2)
1615
-	 * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
1616
-	 *                        and email2@maildomain2
1617
-	 *
1618
-	 * Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
1619
-	 * [
1620
-	 *  users  => [
1621
-	 *      'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
1622
-	 *      'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
1623
-	 *      'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
1624
-	 *  ],
1625
-	 *  remote => [
1626
-	 *      'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
1627
-	 *      'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
1628
-	 *  ],
1629
-	 *  public => bool
1630
-	 *  mail => [
1631
-	 *      'email1@maildomain1' => ['node_id' => 42, 'token' => 'aBcDeFg'],
1632
-	 *      'email2@maildomain2' => ['node_id' => 23, 'token' => 'hIjKlMn'],
1633
-	 *  ]
1634
-	 * ]
1635
-	 *
1636
-	 * The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
1637
-	 * [
1638
-	 *  users  => ['user1', 'user2', 'user4'],
1639
-	 *  remote => bool,
1640
-	 *  public => bool
1641
-	 *  mail => ['email1@maildomain1', 'email2@maildomain2']
1642
-	 * ]
1643
-	 *
1644
-	 * This is required for encryption/activity
1645
-	 *
1646
-	 * @param \OCP\Files\Node $path
1647
-	 * @param bool $recursive Should we check all parent folders as well
1648
-	 * @param bool $currentAccess Ensure the recipient has access to the file (e.g. did not unshare it)
1649
-	 * @return array
1650
-	 */
1651
-	public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false) {
1652
-		$owner = $path->getOwner();
1653
-
1654
-		if ($owner === null) {
1655
-			return [];
1656
-		}
1657
-
1658
-		$owner = $owner->getUID();
1659
-
1660
-		if ($currentAccess) {
1661
-			$al = ['users' => [], 'remote' => [], 'public' => false, 'mail' => []];
1662
-		} else {
1663
-			$al = ['users' => [], 'remote' => false, 'public' => false, 'mail' => []];
1664
-		}
1665
-		if (!$this->userManager->userExists($owner)) {
1666
-			return $al;
1667
-		}
1668
-
1669
-		//Get node for the owner and correct the owner in case of external storage
1670
-		$userFolder = $this->rootFolder->getUserFolder($owner);
1671
-		if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) {
1672
-			$path = $userFolder->getFirstNodeById($path->getId());
1673
-			if ($path === null || $path->getOwner() === null) {
1674
-				return [];
1675
-			}
1676
-			$owner = $path->getOwner()->getUID();
1677
-		}
1678
-
1679
-		$providers = $this->factory->getAllProviders();
1680
-
1681
-		/** @var Node[] $nodes */
1682
-		$nodes = [];
1683
-
1684
-
1685
-		if ($currentAccess) {
1686
-			$ownerPath = $path->getPath();
1687
-			$ownerPath = explode('/', $ownerPath, 4);
1688
-			if (count($ownerPath) < 4) {
1689
-				$ownerPath = '';
1690
-			} else {
1691
-				$ownerPath = $ownerPath[3];
1692
-			}
1693
-			$al['users'][$owner] = [
1694
-				'node_id' => $path->getId(),
1695
-				'node_path' => '/' . $ownerPath,
1696
-			];
1697
-		} else {
1698
-			$al['users'][] = $owner;
1699
-		}
1700
-
1701
-		// Collect all the shares
1702
-		while ($path->getPath() !== $userFolder->getPath()) {
1703
-			$nodes[] = $path;
1704
-			if (!$recursive) {
1705
-				break;
1706
-			}
1707
-			$path = $path->getParent();
1708
-		}
1709
-
1710
-		foreach ($providers as $provider) {
1711
-			$tmp = $provider->getAccessList($nodes, $currentAccess);
1712
-
1713
-			foreach ($tmp as $k => $v) {
1714
-				if (isset($al[$k])) {
1715
-					if (is_array($al[$k])) {
1716
-						if ($currentAccess) {
1717
-							$al[$k] += $v;
1718
-						} else {
1719
-							$al[$k] = array_merge($al[$k], $v);
1720
-							$al[$k] = array_unique($al[$k]);
1721
-							$al[$k] = array_values($al[$k]);
1722
-						}
1723
-					} else {
1724
-						$al[$k] = $al[$k] || $v;
1725
-					}
1726
-				} else {
1727
-					$al[$k] = $v;
1728
-				}
1729
-			}
1730
-		}
1731
-
1732
-		return $al;
1733
-	}
1734
-
1735
-	/**
1736
-	 * Create a new share
1737
-	 *
1738
-	 * @return IShare
1739
-	 */
1740
-	public function newShare() {
1741
-		return new \OC\Share20\Share($this->rootFolder, $this->userManager);
1742
-	}
1743
-
1744
-	/**
1745
-	 * Is the share API enabled
1746
-	 *
1747
-	 * @return bool
1748
-	 */
1749
-	public function shareApiEnabled() {
1750
-		return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
1751
-	}
1752
-
1753
-	/**
1754
-	 * Is public link sharing enabled
1755
-	 *
1756
-	 * @param ?IUser $user User to check against group exclusions, defaults to current session user
1757
-	 * @return bool
1758
-	 */
1759
-	public function shareApiAllowLinks(?IUser $user = null) {
1760
-		if ($this->config->getAppValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
1761
-			return false;
1762
-		}
1763
-
1764
-		$user = $user ?? $this->userSession->getUser();
1765
-		if ($user) {
1766
-			$excludedGroups = json_decode($this->config->getAppValue('core', 'shareapi_allow_links_exclude_groups', '[]'));
1767
-			if ($excludedGroups) {
1768
-				$userGroups = $this->groupManager->getUserGroupIds($user);
1769
-				return !(bool)array_intersect($excludedGroups, $userGroups);
1770
-			}
1771
-		}
1772
-
1773
-		return true;
1774
-	}
1775
-
1776
-	/**
1777
-	 * Check if a specific user can create link shares
1778
-	 *
1779
-	 * @param IUser $user The user to check
1780
-	 * @return bool
1781
-	 */
1782
-	protected function userCanCreateLinkShares(IUser $user): bool {
1783
-		return $this->shareApiAllowLinks($user);
1784
-	}
1785
-
1786
-	/**
1787
-	 * Is password on public link requires
1788
-	 *
1789
-	 * @param bool Check group membership exclusion
1790
-	 * @return bool
1791
-	 */
1792
-	public function shareApiLinkEnforcePassword(bool $checkGroupMembership = true) {
1793
-		$excludedGroups = $this->config->getAppValue('core', 'shareapi_enforce_links_password_excluded_groups', '');
1794
-		if ($excludedGroups !== '' && $checkGroupMembership) {
1795
-			$excludedGroups = json_decode($excludedGroups);
1796
-			$user = $this->userSession->getUser();
1797
-			if ($user) {
1798
-				$userGroups = $this->groupManager->getUserGroupIds($user);
1799
-				if ((bool)array_intersect($excludedGroups, $userGroups)) {
1800
-					return false;
1801
-				}
1802
-			}
1803
-		}
1804
-		return $this->appConfig->getValueBool('core', ConfigLexicon::SHARE_LINK_PASSWORD_ENFORCED);
1805
-	}
1806
-
1807
-	/**
1808
-	 * Is default link expire date enabled
1809
-	 *
1810
-	 * @return bool
1811
-	 */
1812
-	public function shareApiLinkDefaultExpireDate() {
1813
-		return $this->appConfig->getValueBool('core', ConfigLexicon::SHARE_LINK_EXPIRE_DATE_DEFAULT);
1814
-	}
1815
-
1816
-	/**
1817
-	 * Is default link expire date enforced
1818
-	 *
1819
-	 * @return bool
1820
-	 */
1821
-	public function shareApiLinkDefaultExpireDateEnforced() {
1822
-		return $this->shareApiLinkDefaultExpireDate()
1823
-			&& $this->appConfig->getValueBool('core', ConfigLexicon::SHARE_LINK_EXPIRE_DATE_ENFORCED);
1824
-	}
1825
-
1826
-
1827
-	/**
1828
-	 * Number of default link expire days
1829
-	 *
1830
-	 * @return int
1831
-	 */
1832
-	public function shareApiLinkDefaultExpireDays() {
1833
-		return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1834
-	}
1835
-
1836
-	/**
1837
-	 * Is default internal expire date enabled
1838
-	 *
1839
-	 * @return bool
1840
-	 */
1841
-	public function shareApiInternalDefaultExpireDate(): bool {
1842
-		return $this->config->getAppValue('core', 'shareapi_default_internal_expire_date', 'no') === 'yes';
1843
-	}
1844
-
1845
-	/**
1846
-	 * Is default remote expire date enabled
1847
-	 *
1848
-	 * @return bool
1849
-	 */
1850
-	public function shareApiRemoteDefaultExpireDate(): bool {
1851
-		return $this->config->getAppValue('core', 'shareapi_default_remote_expire_date', 'no') === 'yes';
1852
-	}
1853
-
1854
-	/**
1855
-	 * Is default expire date enforced
1856
-	 *
1857
-	 * @return bool
1858
-	 */
1859
-	public function shareApiInternalDefaultExpireDateEnforced(): bool {
1860
-		return $this->shareApiInternalDefaultExpireDate()
1861
-			&& $this->config->getAppValue('core', 'shareapi_enforce_internal_expire_date', 'no') === 'yes';
1862
-	}
1863
-
1864
-	/**
1865
-	 * Is default expire date enforced for remote shares
1866
-	 *
1867
-	 * @return bool
1868
-	 */
1869
-	public function shareApiRemoteDefaultExpireDateEnforced(): bool {
1870
-		return $this->shareApiRemoteDefaultExpireDate()
1871
-			&& $this->config->getAppValue('core', 'shareapi_enforce_remote_expire_date', 'no') === 'yes';
1872
-	}
1873
-
1874
-	/**
1875
-	 * Number of default expire days
1876
-	 *
1877
-	 * @return int
1878
-	 */
1879
-	public function shareApiInternalDefaultExpireDays(): int {
1880
-		return (int)$this->config->getAppValue('core', 'shareapi_internal_expire_after_n_days', '7');
1881
-	}
1882
-
1883
-	/**
1884
-	 * Number of default expire days for remote shares
1885
-	 *
1886
-	 * @return int
1887
-	 */
1888
-	public function shareApiRemoteDefaultExpireDays(): int {
1889
-		return (int)$this->config->getAppValue('core', 'shareapi_remote_expire_after_n_days', '7');
1890
-	}
1891
-
1892
-	/**
1893
-	 * Allow public upload on link shares
1894
-	 *
1895
-	 * @return bool
1896
-	 */
1897
-	public function shareApiLinkAllowPublicUpload() {
1898
-		return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
1899
-	}
1900
-
1901
-	/**
1902
-	 * check if user can only share with group members
1903
-	 *
1904
-	 * @return bool
1905
-	 */
1906
-	public function shareWithGroupMembersOnly() {
1907
-		return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
1908
-	}
1909
-
1910
-	/**
1911
-	 * If shareWithGroupMembersOnly is enabled, return an optional
1912
-	 * list of groups that must be excluded from the principle of
1913
-	 * belonging to the same group.
1914
-	 *
1915
-	 * @return array
1916
-	 */
1917
-	public function shareWithGroupMembersOnlyExcludeGroupsList() {
1918
-		if (!$this->shareWithGroupMembersOnly()) {
1919
-			return [];
1920
-		}
1921
-		$excludeGroups = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', '');
1922
-		return json_decode($excludeGroups, true) ?? [];
1923
-	}
1924
-
1925
-	/**
1926
-	 * Check if users can share with groups
1927
-	 *
1928
-	 * @return bool
1929
-	 */
1930
-	public function allowGroupSharing() {
1931
-		return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
1932
-	}
1933
-
1934
-	public function allowEnumeration(): bool {
1935
-		return $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
1936
-	}
1937
-
1938
-	public function limitEnumerationToGroups(): bool {
1939
-		return $this->allowEnumeration()
1940
-			&& $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
1941
-	}
1942
-
1943
-	public function limitEnumerationToPhone(): bool {
1944
-		return $this->allowEnumeration()
1945
-			&& $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
1946
-	}
1947
-
1948
-	public function allowEnumerationFullMatch(): bool {
1949
-		return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes';
1950
-	}
1951
-
1952
-	public function matchEmail(): bool {
1953
-		return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes') === 'yes';
1954
-	}
1955
-
1956
-	public function matchUserId(): bool {
1957
-		return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_userid', 'yes') === 'yes';
1958
-	}
1959
-
1960
-	public function ignoreSecondDisplayName(): bool {
1961
-		return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn', 'no') === 'yes';
1962
-	}
1963
-
1964
-	public function allowCustomTokens(): bool {
1965
-		return $this->appConfig->getValueBool('core', ConfigLexicon::SHARE_CUSTOM_TOKEN);
1966
-	}
1967
-
1968
-	public function allowViewWithoutDownload(): bool {
1969
-		return $this->appConfig->getValueBool('core', 'shareapi_allow_view_without_download', true);
1970
-	}
1971
-
1972
-	public function currentUserCanEnumerateTargetUser(?IUser $currentUser, IUser $targetUser): bool {
1973
-		if ($this->allowEnumerationFullMatch()) {
1974
-			return true;
1975
-		}
1976
-
1977
-		if (!$this->allowEnumeration()) {
1978
-			return false;
1979
-		}
1980
-
1981
-		if (!$this->limitEnumerationToPhone() && !$this->limitEnumerationToGroups()) {
1982
-			// Enumeration is enabled and not restricted: OK
1983
-			return true;
1984
-		}
1985
-
1986
-		if (!$currentUser instanceof IUser) {
1987
-			// Enumeration restrictions require an account
1988
-			return false;
1989
-		}
1990
-
1991
-		// Enumeration is limited to phone match
1992
-		if ($this->limitEnumerationToPhone() && $this->knownUserService->isKnownToUser($currentUser->getUID(), $targetUser->getUID())) {
1993
-			return true;
1994
-		}
1995
-
1996
-		// Enumeration is limited to groups
1997
-		if ($this->limitEnumerationToGroups()) {
1998
-			$currentUserGroupIds = $this->groupManager->getUserGroupIds($currentUser);
1999
-			$targetUserGroupIds = $this->groupManager->getUserGroupIds($targetUser);
2000
-			if (!empty(array_intersect($currentUserGroupIds, $targetUserGroupIds))) {
2001
-				return true;
2002
-			}
2003
-		}
2004
-
2005
-		return false;
2006
-	}
2007
-
2008
-	/**
2009
-	 * Check if sharing is disabled for the current user
2010
-	 */
2011
-	public function sharingDisabledForUser(?string $userId): bool {
2012
-		return $this->shareDisableChecker->sharingDisabledForUser($userId);
2013
-	}
2014
-
2015
-	/**
2016
-	 * @inheritdoc
2017
-	 */
2018
-	public function outgoingServer2ServerSharesAllowed() {
2019
-		return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
2020
-	}
2021
-
2022
-	/**
2023
-	 * @inheritdoc
2024
-	 */
2025
-	public function outgoingServer2ServerGroupSharesAllowed() {
2026
-		return $this->config->getAppValue('files_sharing', 'outgoing_server2server_group_share_enabled', 'no') === 'yes';
2027
-	}
2028
-
2029
-	/**
2030
-	 * @inheritdoc
2031
-	 */
2032
-	public function shareProviderExists($shareType) {
2033
-		try {
2034
-			$this->factory->getProviderForType($shareType);
2035
-		} catch (ProviderException $e) {
2036
-			return false;
2037
-		}
2038
-
2039
-		return true;
2040
-	}
2041
-
2042
-	public function registerShareProvider(string $shareProviderClass): void {
2043
-		$this->factory->registerProvider($shareProviderClass);
2044
-	}
2045
-
2046
-	public function getAllShares(): iterable {
2047
-		$providers = $this->factory->getAllProviders();
2048
-
2049
-		foreach ($providers as $provider) {
2050
-			yield from $provider->getAllShares();
2051
-		}
2052
-	}
2053
-
2054
-	public function generateToken(): string {
2055
-		// Initial token length
2056
-		$tokenLength = \OC\Share\Helper::getTokenLength();
2057
-
2058
-		do {
2059
-			$tokenExists = false;
2060
-
2061
-			for ($i = 0; $i <= 2; $i++) {
2062
-				// Generate a new token
2063
-				$token = $this->secureRandom->generate(
2064
-					$tokenLength,
2065
-					ISecureRandom::CHAR_HUMAN_READABLE,
2066
-				);
2067
-
2068
-				try {
2069
-					// Try to fetch a share with the generated token
2070
-					$this->getShareByToken($token);
2071
-					$tokenExists = true; // Token exists, we need to try again
2072
-				} catch (ShareNotFound $e) {
2073
-					// Token is unique, exit the loop
2074
-					$tokenExists = false;
2075
-					break;
2076
-				}
2077
-			}
2078
-
2079
-			// If we've reached the maximum attempts and the token still exists, increase the token length
2080
-			if ($tokenExists) {
2081
-				$tokenLength++;
2082
-
2083
-				// Check if the token length exceeds the maximum allowed length
2084
-				if ($tokenLength > \OC\Share\Constants::MAX_TOKEN_LENGTH) {
2085
-					throw new ShareTokenException('Unable to generate a unique share token. Maximum token length exceeded.');
2086
-				}
2087
-			}
2088
-		} while ($tokenExists);
2089
-
2090
-		return $token;
2091
-	}
2092
-
2093
-	private function dispatchEvent(Event $event, string $name): void {
2094
-		try {
2095
-			$this->dispatcher->dispatchTyped($event);
2096
-		} catch (\Exception $e) {
2097
-			$this->logger->error("Error while sending ' . $name . ' event", ['exception' => $e]);
2098
-		}
2099
-	}
1472
+        if (($share->getShareType() === IShare::TYPE_LINK || $share->getShareType() === IShare::TYPE_EMAIL)
1473
+            && $share->getNodeType() === 'folder' && !$this->shareApiLinkAllowPublicUpload()) {
1474
+            $share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
1475
+        }
1476
+
1477
+        return $share;
1478
+    }
1479
+
1480
+    /**
1481
+     * Check expire date and disabled owner
1482
+     *
1483
+     * @throws ShareNotFound
1484
+     */
1485
+    protected function checkShare(IShare $share): void {
1486
+        if ($share->isExpired()) {
1487
+            $this->deleteShare($share);
1488
+            throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1489
+        }
1490
+        if ($this->config->getAppValue('files_sharing', 'hide_disabled_user_shares', 'no') === 'yes') {
1491
+            $uids = array_unique([$share->getShareOwner(),$share->getSharedBy()]);
1492
+            foreach ($uids as $uid) {
1493
+                $user = $this->userManager->get($uid);
1494
+                if ($user?->isEnabled() === false) {
1495
+                    throw new ShareNotFound($this->l->t('The requested share comes from a disabled user'));
1496
+                }
1497
+            }
1498
+        }
1499
+
1500
+        // For link and email shares, verify the share owner can still create such shares
1501
+        if ($share->getShareType() === IShare::TYPE_LINK || $share->getShareType() === IShare::TYPE_EMAIL) {
1502
+            $shareOwner = $this->userManager->get($share->getShareOwner());
1503
+            if ($shareOwner === null) {
1504
+                throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1505
+            }
1506
+            if (!$this->userCanCreateLinkShares($shareOwner)) {
1507
+                throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1508
+            }
1509
+        }
1510
+    }
1511
+
1512
+    /**
1513
+     * Verify the password of a public share
1514
+     *
1515
+     * @param IShare $share
1516
+     * @param ?string $password
1517
+     * @return bool
1518
+     */
1519
+    public function checkPassword(IShare $share, $password) {
1520
+
1521
+        // if there is no password on the share object / passsword is null, there is nothing to check
1522
+        if ($password === null || $share->getPassword() === null) {
1523
+            return false;
1524
+        }
1525
+
1526
+        // Makes sure password hasn't expired
1527
+        $expirationTime = $share->getPasswordExpirationTime();
1528
+        if ($expirationTime !== null && $expirationTime < new \DateTime()) {
1529
+            return false;
1530
+        }
1531
+
1532
+        $newHash = '';
1533
+        if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
1534
+            return false;
1535
+        }
1536
+
1537
+        if (!empty($newHash)) {
1538
+            $share->setPassword($newHash);
1539
+            $provider = $this->factory->getProviderForType($share->getShareType());
1540
+            $provider->update($share);
1541
+        }
1542
+
1543
+        return true;
1544
+    }
1545
+
1546
+    /**
1547
+     * @inheritdoc
1548
+     */
1549
+    public function userDeleted($uid) {
1550
+        $types = [IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK, IShare::TYPE_REMOTE, IShare::TYPE_EMAIL];
1551
+
1552
+        foreach ($types as $type) {
1553
+            try {
1554
+                $provider = $this->factory->getProviderForType($type);
1555
+            } catch (ProviderException $e) {
1556
+                continue;
1557
+            }
1558
+            $provider->userDeleted($uid, $type);
1559
+        }
1560
+    }
1561
+
1562
+    /**
1563
+     * @inheritdoc
1564
+     */
1565
+    public function groupDeleted($gid) {
1566
+        foreach ([IShare::TYPE_GROUP, IShare::TYPE_REMOTE_GROUP] as $type) {
1567
+            try {
1568
+                $provider = $this->factory->getProviderForType($type);
1569
+            } catch (ProviderException $e) {
1570
+                continue;
1571
+            }
1572
+            $provider->groupDeleted($gid);
1573
+        }
1574
+
1575
+        $excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1576
+        if ($excludedGroups === '') {
1577
+            return;
1578
+        }
1579
+
1580
+        $excludedGroups = json_decode($excludedGroups, true);
1581
+        if (json_last_error() !== JSON_ERROR_NONE) {
1582
+            return;
1583
+        }
1584
+
1585
+        $excludedGroups = array_diff($excludedGroups, [$gid]);
1586
+        $this->config->setAppValue('core', 'shareapi_exclude_groups_list', json_encode($excludedGroups));
1587
+    }
1588
+
1589
+    /**
1590
+     * @inheritdoc
1591
+     */
1592
+    public function userDeletedFromGroup($uid, $gid) {
1593
+        foreach ([IShare::TYPE_GROUP, IShare::TYPE_REMOTE_GROUP] as $type) {
1594
+            try {
1595
+                $provider = $this->factory->getProviderForType($type);
1596
+            } catch (ProviderException $e) {
1597
+                continue;
1598
+            }
1599
+            $provider->userDeletedFromGroup($uid, $gid);
1600
+        }
1601
+    }
1602
+
1603
+    /**
1604
+     * Get access list to a path. This means
1605
+     * all the users that can access a given path.
1606
+     *
1607
+     * Consider:
1608
+     * -root
1609
+     * |-folder1 (23)
1610
+     *  |-folder2 (32)
1611
+     *   |-fileA (42)
1612
+     *
1613
+     * fileA is shared with user1 and user1@server1 and email1@maildomain1
1614
+     * folder2 is shared with group2 (user4 is a member of group2)
1615
+     * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
1616
+     *                        and email2@maildomain2
1617
+     *
1618
+     * Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
1619
+     * [
1620
+     *  users  => [
1621
+     *      'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
1622
+     *      'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
1623
+     *      'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
1624
+     *  ],
1625
+     *  remote => [
1626
+     *      'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
1627
+     *      'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
1628
+     *  ],
1629
+     *  public => bool
1630
+     *  mail => [
1631
+     *      'email1@maildomain1' => ['node_id' => 42, 'token' => 'aBcDeFg'],
1632
+     *      'email2@maildomain2' => ['node_id' => 23, 'token' => 'hIjKlMn'],
1633
+     *  ]
1634
+     * ]
1635
+     *
1636
+     * The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
1637
+     * [
1638
+     *  users  => ['user1', 'user2', 'user4'],
1639
+     *  remote => bool,
1640
+     *  public => bool
1641
+     *  mail => ['email1@maildomain1', 'email2@maildomain2']
1642
+     * ]
1643
+     *
1644
+     * This is required for encryption/activity
1645
+     *
1646
+     * @param \OCP\Files\Node $path
1647
+     * @param bool $recursive Should we check all parent folders as well
1648
+     * @param bool $currentAccess Ensure the recipient has access to the file (e.g. did not unshare it)
1649
+     * @return array
1650
+     */
1651
+    public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false) {
1652
+        $owner = $path->getOwner();
1653
+
1654
+        if ($owner === null) {
1655
+            return [];
1656
+        }
1657
+
1658
+        $owner = $owner->getUID();
1659
+
1660
+        if ($currentAccess) {
1661
+            $al = ['users' => [], 'remote' => [], 'public' => false, 'mail' => []];
1662
+        } else {
1663
+            $al = ['users' => [], 'remote' => false, 'public' => false, 'mail' => []];
1664
+        }
1665
+        if (!$this->userManager->userExists($owner)) {
1666
+            return $al;
1667
+        }
1668
+
1669
+        //Get node for the owner and correct the owner in case of external storage
1670
+        $userFolder = $this->rootFolder->getUserFolder($owner);
1671
+        if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) {
1672
+            $path = $userFolder->getFirstNodeById($path->getId());
1673
+            if ($path === null || $path->getOwner() === null) {
1674
+                return [];
1675
+            }
1676
+            $owner = $path->getOwner()->getUID();
1677
+        }
1678
+
1679
+        $providers = $this->factory->getAllProviders();
1680
+
1681
+        /** @var Node[] $nodes */
1682
+        $nodes = [];
1683
+
1684
+
1685
+        if ($currentAccess) {
1686
+            $ownerPath = $path->getPath();
1687
+            $ownerPath = explode('/', $ownerPath, 4);
1688
+            if (count($ownerPath) < 4) {
1689
+                $ownerPath = '';
1690
+            } else {
1691
+                $ownerPath = $ownerPath[3];
1692
+            }
1693
+            $al['users'][$owner] = [
1694
+                'node_id' => $path->getId(),
1695
+                'node_path' => '/' . $ownerPath,
1696
+            ];
1697
+        } else {
1698
+            $al['users'][] = $owner;
1699
+        }
1700
+
1701
+        // Collect all the shares
1702
+        while ($path->getPath() !== $userFolder->getPath()) {
1703
+            $nodes[] = $path;
1704
+            if (!$recursive) {
1705
+                break;
1706
+            }
1707
+            $path = $path->getParent();
1708
+        }
1709
+
1710
+        foreach ($providers as $provider) {
1711
+            $tmp = $provider->getAccessList($nodes, $currentAccess);
1712
+
1713
+            foreach ($tmp as $k => $v) {
1714
+                if (isset($al[$k])) {
1715
+                    if (is_array($al[$k])) {
1716
+                        if ($currentAccess) {
1717
+                            $al[$k] += $v;
1718
+                        } else {
1719
+                            $al[$k] = array_merge($al[$k], $v);
1720
+                            $al[$k] = array_unique($al[$k]);
1721
+                            $al[$k] = array_values($al[$k]);
1722
+                        }
1723
+                    } else {
1724
+                        $al[$k] = $al[$k] || $v;
1725
+                    }
1726
+                } else {
1727
+                    $al[$k] = $v;
1728
+                }
1729
+            }
1730
+        }
1731
+
1732
+        return $al;
1733
+    }
1734
+
1735
+    /**
1736
+     * Create a new share
1737
+     *
1738
+     * @return IShare
1739
+     */
1740
+    public function newShare() {
1741
+        return new \OC\Share20\Share($this->rootFolder, $this->userManager);
1742
+    }
1743
+
1744
+    /**
1745
+     * Is the share API enabled
1746
+     *
1747
+     * @return bool
1748
+     */
1749
+    public function shareApiEnabled() {
1750
+        return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
1751
+    }
1752
+
1753
+    /**
1754
+     * Is public link sharing enabled
1755
+     *
1756
+     * @param ?IUser $user User to check against group exclusions, defaults to current session user
1757
+     * @return bool
1758
+     */
1759
+    public function shareApiAllowLinks(?IUser $user = null) {
1760
+        if ($this->config->getAppValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
1761
+            return false;
1762
+        }
1763
+
1764
+        $user = $user ?? $this->userSession->getUser();
1765
+        if ($user) {
1766
+            $excludedGroups = json_decode($this->config->getAppValue('core', 'shareapi_allow_links_exclude_groups', '[]'));
1767
+            if ($excludedGroups) {
1768
+                $userGroups = $this->groupManager->getUserGroupIds($user);
1769
+                return !(bool)array_intersect($excludedGroups, $userGroups);
1770
+            }
1771
+        }
1772
+
1773
+        return true;
1774
+    }
1775
+
1776
+    /**
1777
+     * Check if a specific user can create link shares
1778
+     *
1779
+     * @param IUser $user The user to check
1780
+     * @return bool
1781
+     */
1782
+    protected function userCanCreateLinkShares(IUser $user): bool {
1783
+        return $this->shareApiAllowLinks($user);
1784
+    }
1785
+
1786
+    /**
1787
+     * Is password on public link requires
1788
+     *
1789
+     * @param bool Check group membership exclusion
1790
+     * @return bool
1791
+     */
1792
+    public function shareApiLinkEnforcePassword(bool $checkGroupMembership = true) {
1793
+        $excludedGroups = $this->config->getAppValue('core', 'shareapi_enforce_links_password_excluded_groups', '');
1794
+        if ($excludedGroups !== '' && $checkGroupMembership) {
1795
+            $excludedGroups = json_decode($excludedGroups);
1796
+            $user = $this->userSession->getUser();
1797
+            if ($user) {
1798
+                $userGroups = $this->groupManager->getUserGroupIds($user);
1799
+                if ((bool)array_intersect($excludedGroups, $userGroups)) {
1800
+                    return false;
1801
+                }
1802
+            }
1803
+        }
1804
+        return $this->appConfig->getValueBool('core', ConfigLexicon::SHARE_LINK_PASSWORD_ENFORCED);
1805
+    }
1806
+
1807
+    /**
1808
+     * Is default link expire date enabled
1809
+     *
1810
+     * @return bool
1811
+     */
1812
+    public function shareApiLinkDefaultExpireDate() {
1813
+        return $this->appConfig->getValueBool('core', ConfigLexicon::SHARE_LINK_EXPIRE_DATE_DEFAULT);
1814
+    }
1815
+
1816
+    /**
1817
+     * Is default link expire date enforced
1818
+     *
1819
+     * @return bool
1820
+     */
1821
+    public function shareApiLinkDefaultExpireDateEnforced() {
1822
+        return $this->shareApiLinkDefaultExpireDate()
1823
+            && $this->appConfig->getValueBool('core', ConfigLexicon::SHARE_LINK_EXPIRE_DATE_ENFORCED);
1824
+    }
1825
+
1826
+
1827
+    /**
1828
+     * Number of default link expire days
1829
+     *
1830
+     * @return int
1831
+     */
1832
+    public function shareApiLinkDefaultExpireDays() {
1833
+        return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1834
+    }
1835
+
1836
+    /**
1837
+     * Is default internal expire date enabled
1838
+     *
1839
+     * @return bool
1840
+     */
1841
+    public function shareApiInternalDefaultExpireDate(): bool {
1842
+        return $this->config->getAppValue('core', 'shareapi_default_internal_expire_date', 'no') === 'yes';
1843
+    }
1844
+
1845
+    /**
1846
+     * Is default remote expire date enabled
1847
+     *
1848
+     * @return bool
1849
+     */
1850
+    public function shareApiRemoteDefaultExpireDate(): bool {
1851
+        return $this->config->getAppValue('core', 'shareapi_default_remote_expire_date', 'no') === 'yes';
1852
+    }
1853
+
1854
+    /**
1855
+     * Is default expire date enforced
1856
+     *
1857
+     * @return bool
1858
+     */
1859
+    public function shareApiInternalDefaultExpireDateEnforced(): bool {
1860
+        return $this->shareApiInternalDefaultExpireDate()
1861
+            && $this->config->getAppValue('core', 'shareapi_enforce_internal_expire_date', 'no') === 'yes';
1862
+    }
1863
+
1864
+    /**
1865
+     * Is default expire date enforced for remote shares
1866
+     *
1867
+     * @return bool
1868
+     */
1869
+    public function shareApiRemoteDefaultExpireDateEnforced(): bool {
1870
+        return $this->shareApiRemoteDefaultExpireDate()
1871
+            && $this->config->getAppValue('core', 'shareapi_enforce_remote_expire_date', 'no') === 'yes';
1872
+    }
1873
+
1874
+    /**
1875
+     * Number of default expire days
1876
+     *
1877
+     * @return int
1878
+     */
1879
+    public function shareApiInternalDefaultExpireDays(): int {
1880
+        return (int)$this->config->getAppValue('core', 'shareapi_internal_expire_after_n_days', '7');
1881
+    }
1882
+
1883
+    /**
1884
+     * Number of default expire days for remote shares
1885
+     *
1886
+     * @return int
1887
+     */
1888
+    public function shareApiRemoteDefaultExpireDays(): int {
1889
+        return (int)$this->config->getAppValue('core', 'shareapi_remote_expire_after_n_days', '7');
1890
+    }
1891
+
1892
+    /**
1893
+     * Allow public upload on link shares
1894
+     *
1895
+     * @return bool
1896
+     */
1897
+    public function shareApiLinkAllowPublicUpload() {
1898
+        return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
1899
+    }
1900
+
1901
+    /**
1902
+     * check if user can only share with group members
1903
+     *
1904
+     * @return bool
1905
+     */
1906
+    public function shareWithGroupMembersOnly() {
1907
+        return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
1908
+    }
1909
+
1910
+    /**
1911
+     * If shareWithGroupMembersOnly is enabled, return an optional
1912
+     * list of groups that must be excluded from the principle of
1913
+     * belonging to the same group.
1914
+     *
1915
+     * @return array
1916
+     */
1917
+    public function shareWithGroupMembersOnlyExcludeGroupsList() {
1918
+        if (!$this->shareWithGroupMembersOnly()) {
1919
+            return [];
1920
+        }
1921
+        $excludeGroups = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', '');
1922
+        return json_decode($excludeGroups, true) ?? [];
1923
+    }
1924
+
1925
+    /**
1926
+     * Check if users can share with groups
1927
+     *
1928
+     * @return bool
1929
+     */
1930
+    public function allowGroupSharing() {
1931
+        return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
1932
+    }
1933
+
1934
+    public function allowEnumeration(): bool {
1935
+        return $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
1936
+    }
1937
+
1938
+    public function limitEnumerationToGroups(): bool {
1939
+        return $this->allowEnumeration()
1940
+            && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
1941
+    }
1942
+
1943
+    public function limitEnumerationToPhone(): bool {
1944
+        return $this->allowEnumeration()
1945
+            && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
1946
+    }
1947
+
1948
+    public function allowEnumerationFullMatch(): bool {
1949
+        return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes';
1950
+    }
1951
+
1952
+    public function matchEmail(): bool {
1953
+        return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes') === 'yes';
1954
+    }
1955
+
1956
+    public function matchUserId(): bool {
1957
+        return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_userid', 'yes') === 'yes';
1958
+    }
1959
+
1960
+    public function ignoreSecondDisplayName(): bool {
1961
+        return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn', 'no') === 'yes';
1962
+    }
1963
+
1964
+    public function allowCustomTokens(): bool {
1965
+        return $this->appConfig->getValueBool('core', ConfigLexicon::SHARE_CUSTOM_TOKEN);
1966
+    }
1967
+
1968
+    public function allowViewWithoutDownload(): bool {
1969
+        return $this->appConfig->getValueBool('core', 'shareapi_allow_view_without_download', true);
1970
+    }
1971
+
1972
+    public function currentUserCanEnumerateTargetUser(?IUser $currentUser, IUser $targetUser): bool {
1973
+        if ($this->allowEnumerationFullMatch()) {
1974
+            return true;
1975
+        }
1976
+
1977
+        if (!$this->allowEnumeration()) {
1978
+            return false;
1979
+        }
1980
+
1981
+        if (!$this->limitEnumerationToPhone() && !$this->limitEnumerationToGroups()) {
1982
+            // Enumeration is enabled and not restricted: OK
1983
+            return true;
1984
+        }
1985
+
1986
+        if (!$currentUser instanceof IUser) {
1987
+            // Enumeration restrictions require an account
1988
+            return false;
1989
+        }
1990
+
1991
+        // Enumeration is limited to phone match
1992
+        if ($this->limitEnumerationToPhone() && $this->knownUserService->isKnownToUser($currentUser->getUID(), $targetUser->getUID())) {
1993
+            return true;
1994
+        }
1995
+
1996
+        // Enumeration is limited to groups
1997
+        if ($this->limitEnumerationToGroups()) {
1998
+            $currentUserGroupIds = $this->groupManager->getUserGroupIds($currentUser);
1999
+            $targetUserGroupIds = $this->groupManager->getUserGroupIds($targetUser);
2000
+            if (!empty(array_intersect($currentUserGroupIds, $targetUserGroupIds))) {
2001
+                return true;
2002
+            }
2003
+        }
2004
+
2005
+        return false;
2006
+    }
2007
+
2008
+    /**
2009
+     * Check if sharing is disabled for the current user
2010
+     */
2011
+    public function sharingDisabledForUser(?string $userId): bool {
2012
+        return $this->shareDisableChecker->sharingDisabledForUser($userId);
2013
+    }
2014
+
2015
+    /**
2016
+     * @inheritdoc
2017
+     */
2018
+    public function outgoingServer2ServerSharesAllowed() {
2019
+        return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
2020
+    }
2021
+
2022
+    /**
2023
+     * @inheritdoc
2024
+     */
2025
+    public function outgoingServer2ServerGroupSharesAllowed() {
2026
+        return $this->config->getAppValue('files_sharing', 'outgoing_server2server_group_share_enabled', 'no') === 'yes';
2027
+    }
2028
+
2029
+    /**
2030
+     * @inheritdoc
2031
+     */
2032
+    public function shareProviderExists($shareType) {
2033
+        try {
2034
+            $this->factory->getProviderForType($shareType);
2035
+        } catch (ProviderException $e) {
2036
+            return false;
2037
+        }
2038
+
2039
+        return true;
2040
+    }
2041
+
2042
+    public function registerShareProvider(string $shareProviderClass): void {
2043
+        $this->factory->registerProvider($shareProviderClass);
2044
+    }
2045
+
2046
+    public function getAllShares(): iterable {
2047
+        $providers = $this->factory->getAllProviders();
2048
+
2049
+        foreach ($providers as $provider) {
2050
+            yield from $provider->getAllShares();
2051
+        }
2052
+    }
2053
+
2054
+    public function generateToken(): string {
2055
+        // Initial token length
2056
+        $tokenLength = \OC\Share\Helper::getTokenLength();
2057
+
2058
+        do {
2059
+            $tokenExists = false;
2060
+
2061
+            for ($i = 0; $i <= 2; $i++) {
2062
+                // Generate a new token
2063
+                $token = $this->secureRandom->generate(
2064
+                    $tokenLength,
2065
+                    ISecureRandom::CHAR_HUMAN_READABLE,
2066
+                );
2067
+
2068
+                try {
2069
+                    // Try to fetch a share with the generated token
2070
+                    $this->getShareByToken($token);
2071
+                    $tokenExists = true; // Token exists, we need to try again
2072
+                } catch (ShareNotFound $e) {
2073
+                    // Token is unique, exit the loop
2074
+                    $tokenExists = false;
2075
+                    break;
2076
+                }
2077
+            }
2078
+
2079
+            // If we've reached the maximum attempts and the token still exists, increase the token length
2080
+            if ($tokenExists) {
2081
+                $tokenLength++;
2082
+
2083
+                // Check if the token length exceeds the maximum allowed length
2084
+                if ($tokenLength > \OC\Share\Constants::MAX_TOKEN_LENGTH) {
2085
+                    throw new ShareTokenException('Unable to generate a unique share token. Maximum token length exceeded.');
2086
+                }
2087
+            }
2088
+        } while ($tokenExists);
2089
+
2090
+        return $token;
2091
+    }
2092
+
2093
+    private function dispatchEvent(Event $event, string $name): void {
2094
+        try {
2095
+            $this->dispatcher->dispatchTyped($event);
2096
+        } catch (\Exception $e) {
2097
+            $this->logger->error("Error while sending ' . $name . ' event", ['exception' => $e]);
2098
+        }
2099
+    }
2100 2100
 }
Please login to merge, or discard this patch.
lib/public/Share/IManager.php 1 patch
Indentation   +534 added lines, -534 removed lines patch added patch discarded remove patch
@@ -25,538 +25,538 @@
 block discarded – undo
25 25
  * @since 9.0.0
26 26
  */
27 27
 interface IManager {
28
-	/**
29
-	 * Create a Share
30
-	 *
31
-	 * @param IShare $share
32
-	 * @return IShare The share object
33
-	 * @throws \Exception
34
-	 * @since 9.0.0
35
-	 */
36
-	public function createShare(IShare $share);
37
-
38
-	/**
39
-	 * Update a share.
40
-	 * The target of the share can't be changed this way: use moveShare
41
-	 * The share can't be removed this way (permission 0): use deleteShare
42
-	 * The state can't be changed this way: use acceptShare
43
-	 *
44
-	 * @param IShare $share
45
-	 * @param bool $onlyValid Only updates valid shares, invalid shares will be deleted automatically and are not updated
46
-	 * @return IShare The share object
47
-	 * @throws \InvalidArgumentException
48
-	 * @since 9.0.0
49
-	 */
50
-	public function updateShare(IShare $share, bool $onlyValid = true);
51
-
52
-	/**
53
-	 * Accept a share.
54
-	 *
55
-	 * @param IShare $share
56
-	 * @param string $recipientId
57
-	 * @return IShare The share object
58
-	 * @throws \InvalidArgumentException
59
-	 * @since 18.0.0
60
-	 */
61
-	public function acceptShare(IShare $share, string $recipientId): IShare;
62
-
63
-	/**
64
-	 * Delete a share
65
-	 *
66
-	 * @param IShare $share
67
-	 * @throws ShareNotFound
68
-	 * @throws \InvalidArgumentException
69
-	 * @since 9.0.0
70
-	 */
71
-	public function deleteShare(IShare $share);
72
-
73
-	/**
74
-	 * Unshare a file as the recipient.
75
-	 * This can be different from a regular delete for example when one of
76
-	 * the users in a groups deletes that share. But the provider should
77
-	 * handle this.
78
-	 *
79
-	 * @param IShare $share
80
-	 * @param string $recipientId
81
-	 * @since 9.0.0
82
-	 */
83
-	public function deleteFromSelf(IShare $share, $recipientId);
84
-
85
-	/**
86
-	 * Restore the share when it has been deleted
87
-	 * Certain share types can be restored when they have been deleted
88
-	 * but the provider should properly handle this\
89
-	 *
90
-	 * @param IShare $share The share to restore
91
-	 * @param string $recipientId The user to restore the share for
92
-	 * @return IShare The restored share object
93
-	 * @throws GenericShareException In case restoring the share failed
94
-	 *
95
-	 * @since 14.0.0
96
-	 */
97
-	public function restoreShare(IShare $share, string $recipientId): IShare;
98
-
99
-	/**
100
-	 * Move the share as a recipient of the share.
101
-	 * This is updating the share target. So where the recipient has the share mounted.
102
-	 *
103
-	 * @param IShare $share
104
-	 * @param string $recipientId
105
-	 * @return IShare
106
-	 * @throws \InvalidArgumentException If $share is a link share or the $recipient does not match
107
-	 * @since 9.0.0
108
-	 */
109
-	public function moveShare(IShare $share, $recipientId);
110
-
111
-	/**
112
-	 * Get all shares shared by (initiated) by the provided user in a folder.
113
-	 *
114
-	 * @param string $userId
115
-	 * @param Folder $node
116
-	 * @param bool $reshares
117
-	 * @param bool $shallow Whether the method should stop at the first level, or look into sub-folders.
118
-	 * @return IShare[][] [$fileId => IShare[], ...]
119
-	 * @since 11.0.0
120
-	 */
121
-	public function getSharesInFolder($userId, Folder $node, $reshares = false, $shallow = true);
122
-
123
-	/**
124
-	 * Get shares shared by (initiated) by the provided user.
125
-	 *
126
-	 * @param string $userId
127
-	 * @param int $shareType
128
-	 * @param Node|null $path
129
-	 * @param bool $reshares
130
-	 * @param int $limit The maximum number of returned results, -1 for all results
131
-	 * @param int $offset
132
-	 * @param bool $onlyValid Only returns valid shares, invalid shares will be deleted automatically and are not returned
133
-	 * @return IShare[]
134
-	 * @since 9.0.0
135
-	 */
136
-	public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0, bool $onlyValid = true);
137
-
138
-	/**
139
-	 * Get shares shared with $user.
140
-	 * Filter by $node if provided
141
-	 *
142
-	 * @param string $userId
143
-	 * @param int $shareType
144
-	 * @param Node|null $node
145
-	 * @param int $limit The maximum number of shares returned, -1 for all
146
-	 * @param int $offset
147
-	 * @return IShare[]
148
-	 * @since 9.0.0
149
-	 */
150
-	public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0);
151
-
152
-	/**
153
-	 * Get deleted shares shared with $user.
154
-	 * Filter by $node if provided
155
-	 *
156
-	 * @param string $userId
157
-	 * @param int $shareType
158
-	 * @param Node|null $node
159
-	 * @param int $limit The maximum number of shares returned, -1 for all
160
-	 * @param int $offset
161
-	 * @return IShare[]
162
-	 * @since 14.0.0
163
-	 */
164
-	public function getDeletedSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0);
165
-
166
-	/**
167
-	 * Retrieve a share by the share id.
168
-	 * If the recipient is set make sure to retrieve the file for that user.
169
-	 * This makes sure that if a user has moved/deleted a group share this
170
-	 * is reflected.
171
-	 *
172
-	 * @param string $id
173
-	 * @param string|null $recipient userID of the recipient
174
-	 * @param bool $onlyValid Only returns valid shares, invalid shares will be deleted automatically and are not returned
175
-	 * @return IShare
176
-	 * @throws ShareNotFound
177
-	 * @since 9.0.0
178
-	 */
179
-	public function getShareById($id, $recipient = null, bool $onlyValid = true);
180
-
181
-	/**
182
-	 * Get the share by token possible with password
183
-	 *
184
-	 * @param string $token
185
-	 * @return IShare
186
-	 * @throws ShareNotFound
187
-	 * @since 9.0.0
188
-	 */
189
-	public function getShareByToken($token);
190
-
191
-	/**
192
-	 * Verify the password of a public share
193
-	 *
194
-	 * @param IShare $share
195
-	 * @param ?string $password
196
-	 * @return bool
197
-	 * @since 9.0.0
198
-	 */
199
-	public function checkPassword(IShare $share, $password);
200
-
201
-	/**
202
-	 * The user with UID is deleted.
203
-	 * All share providers have to cleanup the shares with this user as well
204
-	 * as shares owned by this user.
205
-	 * Shares only initiated by this user are fine.
206
-	 *
207
-	 * @param string $uid
208
-	 * @since 9.1.0
209
-	 */
210
-	public function userDeleted($uid);
211
-
212
-	/**
213
-	 * The group with $gid is deleted
214
-	 * We need to clear up all shares to this group
215
-	 *
216
-	 * @param string $gid
217
-	 * @since 9.1.0
218
-	 */
219
-	public function groupDeleted($gid);
220
-
221
-	/**
222
-	 * The user $uid is deleted from the group $gid
223
-	 * All user specific group shares have to be removed
224
-	 *
225
-	 * @param string $uid
226
-	 * @param string $gid
227
-	 * @since 9.1.0
228
-	 */
229
-	public function userDeletedFromGroup($uid, $gid);
230
-
231
-	/**
232
-	 * Get access list to a path. This means
233
-	 * all the users that can access a given path.
234
-	 *
235
-	 * Consider:
236
-	 * -root
237
-	 * |-folder1 (23)
238
-	 *  |-folder2 (32)
239
-	 *   |-fileA (42)
240
-	 *
241
-	 * fileA is shared with user1 and user1@server1 and email1@maildomain1
242
-	 * folder2 is shared with group2 (user4 is a member of group2)
243
-	 * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
244
-	 *                        and email2@maildomain2
245
-	 *
246
-	 * Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
247
-	 * [
248
-	 *  users  => [
249
-	 *      'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
250
-	 *      'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
251
-	 *      'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
252
-	 *  ],
253
-	 *  remote => [
254
-	 *      'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
255
-	 *      'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
256
-	 *  ],
257
-	 *  public => bool
258
-	 *  mail => [
259
-	 *      'email1@maildomain1' => ['node_id' => 42, 'token' => 'aBcDeFg'],
260
-	 *      'email2@maildomain2' => ['node_id' => 23, 'token' => 'hIjKlMn'],
261
-	 *  ]
262
-	 *
263
-	 * The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
264
-	 * [
265
-	 *  users  => ['user1', 'user2', 'user4'],
266
-	 *  remote => bool,
267
-	 *  public => bool
268
-	 *  mail => ['email1@maildomain1', 'email2@maildomain2']
269
-	 * ]
270
-	 *
271
-	 * This is required for encryption/activity
272
-	 *
273
-	 * @param \OCP\Files\Node $path
274
-	 * @param bool $recursive Should we check all parent folders as well
275
-	 * @param bool $currentAccess Should the user have currently access to the file
276
-	 * @return array
277
-	 * @since 12
278
-	 */
279
-	public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false);
280
-
281
-	/**
282
-	 * Instantiates a new share object. This is to be passed to
283
-	 * createShare.
284
-	 *
285
-	 * @return IShare
286
-	 * @since 9.0.0
287
-	 */
288
-	public function newShare();
289
-
290
-	/**
291
-	 * Is the share API enabled
292
-	 *
293
-	 * @return bool
294
-	 * @since 9.0.0
295
-	 */
296
-	public function shareApiEnabled();
297
-
298
-	/**
299
-	 * Is public link sharing enabled
300
-	 *
301
-	 * @param ?IUser $user User to check against group exclusions, defaults to current session user
302
-	 * @return bool
303
-	 * @since 9.0.0
304
-	 * @since 33.0.0 Added optional $user parameter
305
-	 */
306
-	public function shareApiAllowLinks(?IUser $user = null);
307
-
308
-	/**
309
-	 * Is password on public link required
310
-	 *
311
-	 * @param bool $checkGroupMembership Check group membership exclusion
312
-	 * @return bool
313
-	 * @since 9.0.0
314
-	 * @since 24.0.0 Added optional $checkGroupMembership parameter
315
-	 */
316
-	public function shareApiLinkEnforcePassword(bool $checkGroupMembership = true);
317
-
318
-	/**
319
-	 * Is default expire date enabled
320
-	 *
321
-	 * @return bool
322
-	 * @since 9.0.0
323
-	 */
324
-	public function shareApiLinkDefaultExpireDate();
325
-
326
-	/**
327
-	 * Is default expire date enforced
328
-	 *`
329
-	 * @return bool
330
-	 * @since 9.0.0
331
-	 */
332
-	public function shareApiLinkDefaultExpireDateEnforced();
333
-
334
-	/**
335
-	 * Number of default expire days
336
-	 *
337
-	 * @return int
338
-	 * @since 9.0.0
339
-	 */
340
-	public function shareApiLinkDefaultExpireDays();
341
-
342
-	/**
343
-	 * Is default internal expire date enabled
344
-	 *
345
-	 * @return bool
346
-	 * @since 22.0.0
347
-	 */
348
-	public function shareApiInternalDefaultExpireDate(): bool;
349
-
350
-	/**
351
-	 * Is default remote expire date enabled
352
-	 *
353
-	 * @return bool
354
-	 * @since 22.0.0
355
-	 */
356
-	public function shareApiRemoteDefaultExpireDate(): bool;
357
-
358
-	/**
359
-	 * Is default expire date enforced
360
-	 *
361
-	 * @return bool
362
-	 * @since 22.0.0
363
-	 */
364
-	public function shareApiInternalDefaultExpireDateEnforced(): bool;
365
-
366
-	/**
367
-	 * Is default expire date enforced for remote shares
368
-	 *
369
-	 * @return bool
370
-	 * @since 22.0.0
371
-	 */
372
-	public function shareApiRemoteDefaultExpireDateEnforced(): bool;
373
-
374
-	/**
375
-	 * Number of default expire days
376
-	 *
377
-	 * @return int
378
-	 * @since 22.0.0
379
-	 */
380
-	public function shareApiInternalDefaultExpireDays(): int;
381
-
382
-	/**
383
-	 * Number of default expire days for remote shares
384
-	 *
385
-	 * @return int
386
-	 * @since 22.0.0
387
-	 */
388
-	public function shareApiRemoteDefaultExpireDays(): int;
389
-
390
-	/**
391
-	 * Allow public upload on link shares
392
-	 *
393
-	 * @return bool
394
-	 * @since 9.0.0
395
-	 */
396
-	public function shareApiLinkAllowPublicUpload();
397
-
398
-	/**
399
-	 * check if user can only share with group members
400
-	 * @return bool
401
-	 * @since 9.0.0
402
-	 */
403
-	public function shareWithGroupMembersOnly();
404
-
405
-	/**
406
-	 * If shareWithGroupMembersOnly is enabled, return an optional
407
-	 * list of groups that must be excluded from the principle of
408
-	 * belonging to the same group.
409
-	 * @return array
410
-	 * @since 27.0.0
411
-	 */
412
-	public function shareWithGroupMembersOnlyExcludeGroupsList();
413
-
414
-	/**
415
-	 * Check if users can share with groups
416
-	 * @return bool
417
-	 * @since 9.0.1
418
-	 */
419
-	public function allowGroupSharing();
420
-
421
-	/**
422
-	 * Check if user enumeration is allowed
423
-	 *
424
-	 * @return bool
425
-	 * @since 19.0.0
426
-	 */
427
-	public function allowEnumeration(): bool;
428
-
429
-	/**
430
-	 * Check if user enumeration is limited to the users groups
431
-	 *
432
-	 * @return bool
433
-	 * @since 19.0.0
434
-	 */
435
-	public function limitEnumerationToGroups(): bool;
436
-
437
-	/**
438
-	 * Check if user enumeration is limited to the phonebook matches
439
-	 *
440
-	 * @return bool
441
-	 * @since 21.0.1
442
-	 */
443
-	public function limitEnumerationToPhone(): bool;
444
-
445
-	/**
446
-	 * Check if user enumeration is allowed to return also on full match
447
-	 * and ignore limitations to phonebook or groups.
448
-	 *
449
-	 * @return bool
450
-	 * @since 21.0.1
451
-	 */
452
-	public function allowEnumerationFullMatch(): bool;
453
-
454
-	/**
455
-	 * When `allowEnumerationFullMatch` is enabled and `matchEmail` is set,
456
-	 * then also return results for full email matches.
457
-	 *
458
-	 * @return bool
459
-	 * @since 25.0.0
460
-	 */
461
-	public function matchEmail(): bool;
462
-
463
-	/**
464
-	 * When `allowEnumerationFullMatch` is enabled and `matchUserId` is set,
465
-	 * then also return results for full user id matches.
466
-	 *
467
-	 * @return bool
468
-	 * @since 33.0.0
469
-	 */
470
-	public function matchUserId(): bool;
471
-
472
-	/**
473
-	 * When `allowEnumerationFullMatch` is enabled and `ignoreSecondDisplayName` is set,
474
-	 * then the search should ignore matches on the second displayname and only use the first.
475
-	 *
476
-	 * @return bool
477
-	 * @since 25.0.0
478
-	 */
479
-	public function ignoreSecondDisplayName(): bool;
480
-
481
-
482
-	/**
483
-	 * Check if custom tokens are allowed
484
-	 *
485
-	 * @since 31.0.0
486
-	 */
487
-	public function allowCustomTokens(): bool;
488
-
489
-	/**
490
-	 * Check if the current user can view the share
491
-	 * even if the download is disabled.
492
-	 *
493
-	 * @since 32.0.0
494
-	 */
495
-	public function allowViewWithoutDownload(): bool;
496
-
497
-	/**
498
-	 * Check if the current user can enumerate the target user
499
-	 *
500
-	 * @param IUser|null $currentUser
501
-	 * @param IUser $targetUser
502
-	 * @return bool
503
-	 * @since 23.0.0
504
-	 */
505
-	public function currentUserCanEnumerateTargetUser(?IUser $currentUser, IUser $targetUser): bool;
506
-
507
-	/**
508
-	 * Check if sharing is disabled for the given user
509
-	 *
510
-	 * @since 9.0.0
511
-	 */
512
-	public function sharingDisabledForUser(?string $userId): bool;
513
-
514
-	/**
515
-	 * Check if outgoing server2server shares are allowed
516
-	 * @return bool
517
-	 * @since 9.0.0
518
-	 */
519
-	public function outgoingServer2ServerSharesAllowed();
520
-
521
-	/**
522
-	 * Check if outgoing server2server shares are allowed
523
-	 * @return bool
524
-	 * @since 14.0.0
525
-	 */
526
-	public function outgoingServer2ServerGroupSharesAllowed();
527
-
528
-
529
-	/**
530
-	 * Check if a given share provider exists
531
-	 * @param int $shareType
532
-	 * @return bool
533
-	 * @since 11.0.0
534
-	 */
535
-	public function shareProviderExists($shareType);
536
-
537
-	/**
538
-	 * @param string $shareProviderClass
539
-	 * @since 21.0.0
540
-	 */
541
-	public function registerShareProvider(string $shareProviderClass): void;
542
-
543
-	/**
544
-	 * @Internal
545
-	 *
546
-	 * Get all the shares as iterable to reduce memory overhead
547
-	 * Note, since this opens up database cursors the iterable should
548
-	 * be fully itterated.
549
-	 *
550
-	 * @return iterable
551
-	 * @since 18.0.0
552
-	 */
553
-	public function getAllShares(): iterable;
554
-
555
-	/**
556
-	 * Generate a unique share token
557
-	 *
558
-	 * @throws ShareTokenException Failed to generate a unique token
559
-	 * @since 31.0.0
560
-	 */
561
-	public function generateToken(): string;
28
+    /**
29
+     * Create a Share
30
+     *
31
+     * @param IShare $share
32
+     * @return IShare The share object
33
+     * @throws \Exception
34
+     * @since 9.0.0
35
+     */
36
+    public function createShare(IShare $share);
37
+
38
+    /**
39
+     * Update a share.
40
+     * The target of the share can't be changed this way: use moveShare
41
+     * The share can't be removed this way (permission 0): use deleteShare
42
+     * The state can't be changed this way: use acceptShare
43
+     *
44
+     * @param IShare $share
45
+     * @param bool $onlyValid Only updates valid shares, invalid shares will be deleted automatically and are not updated
46
+     * @return IShare The share object
47
+     * @throws \InvalidArgumentException
48
+     * @since 9.0.0
49
+     */
50
+    public function updateShare(IShare $share, bool $onlyValid = true);
51
+
52
+    /**
53
+     * Accept a share.
54
+     *
55
+     * @param IShare $share
56
+     * @param string $recipientId
57
+     * @return IShare The share object
58
+     * @throws \InvalidArgumentException
59
+     * @since 18.0.0
60
+     */
61
+    public function acceptShare(IShare $share, string $recipientId): IShare;
62
+
63
+    /**
64
+     * Delete a share
65
+     *
66
+     * @param IShare $share
67
+     * @throws ShareNotFound
68
+     * @throws \InvalidArgumentException
69
+     * @since 9.0.0
70
+     */
71
+    public function deleteShare(IShare $share);
72
+
73
+    /**
74
+     * Unshare a file as the recipient.
75
+     * This can be different from a regular delete for example when one of
76
+     * the users in a groups deletes that share. But the provider should
77
+     * handle this.
78
+     *
79
+     * @param IShare $share
80
+     * @param string $recipientId
81
+     * @since 9.0.0
82
+     */
83
+    public function deleteFromSelf(IShare $share, $recipientId);
84
+
85
+    /**
86
+     * Restore the share when it has been deleted
87
+     * Certain share types can be restored when they have been deleted
88
+     * but the provider should properly handle this\
89
+     *
90
+     * @param IShare $share The share to restore
91
+     * @param string $recipientId The user to restore the share for
92
+     * @return IShare The restored share object
93
+     * @throws GenericShareException In case restoring the share failed
94
+     *
95
+     * @since 14.0.0
96
+     */
97
+    public function restoreShare(IShare $share, string $recipientId): IShare;
98
+
99
+    /**
100
+     * Move the share as a recipient of the share.
101
+     * This is updating the share target. So where the recipient has the share mounted.
102
+     *
103
+     * @param IShare $share
104
+     * @param string $recipientId
105
+     * @return IShare
106
+     * @throws \InvalidArgumentException If $share is a link share or the $recipient does not match
107
+     * @since 9.0.0
108
+     */
109
+    public function moveShare(IShare $share, $recipientId);
110
+
111
+    /**
112
+     * Get all shares shared by (initiated) by the provided user in a folder.
113
+     *
114
+     * @param string $userId
115
+     * @param Folder $node
116
+     * @param bool $reshares
117
+     * @param bool $shallow Whether the method should stop at the first level, or look into sub-folders.
118
+     * @return IShare[][] [$fileId => IShare[], ...]
119
+     * @since 11.0.0
120
+     */
121
+    public function getSharesInFolder($userId, Folder $node, $reshares = false, $shallow = true);
122
+
123
+    /**
124
+     * Get shares shared by (initiated) by the provided user.
125
+     *
126
+     * @param string $userId
127
+     * @param int $shareType
128
+     * @param Node|null $path
129
+     * @param bool $reshares
130
+     * @param int $limit The maximum number of returned results, -1 for all results
131
+     * @param int $offset
132
+     * @param bool $onlyValid Only returns valid shares, invalid shares will be deleted automatically and are not returned
133
+     * @return IShare[]
134
+     * @since 9.0.0
135
+     */
136
+    public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0, bool $onlyValid = true);
137
+
138
+    /**
139
+     * Get shares shared with $user.
140
+     * Filter by $node if provided
141
+     *
142
+     * @param string $userId
143
+     * @param int $shareType
144
+     * @param Node|null $node
145
+     * @param int $limit The maximum number of shares returned, -1 for all
146
+     * @param int $offset
147
+     * @return IShare[]
148
+     * @since 9.0.0
149
+     */
150
+    public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0);
151
+
152
+    /**
153
+     * Get deleted shares shared with $user.
154
+     * Filter by $node if provided
155
+     *
156
+     * @param string $userId
157
+     * @param int $shareType
158
+     * @param Node|null $node
159
+     * @param int $limit The maximum number of shares returned, -1 for all
160
+     * @param int $offset
161
+     * @return IShare[]
162
+     * @since 14.0.0
163
+     */
164
+    public function getDeletedSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0);
165
+
166
+    /**
167
+     * Retrieve a share by the share id.
168
+     * If the recipient is set make sure to retrieve the file for that user.
169
+     * This makes sure that if a user has moved/deleted a group share this
170
+     * is reflected.
171
+     *
172
+     * @param string $id
173
+     * @param string|null $recipient userID of the recipient
174
+     * @param bool $onlyValid Only returns valid shares, invalid shares will be deleted automatically and are not returned
175
+     * @return IShare
176
+     * @throws ShareNotFound
177
+     * @since 9.0.0
178
+     */
179
+    public function getShareById($id, $recipient = null, bool $onlyValid = true);
180
+
181
+    /**
182
+     * Get the share by token possible with password
183
+     *
184
+     * @param string $token
185
+     * @return IShare
186
+     * @throws ShareNotFound
187
+     * @since 9.0.0
188
+     */
189
+    public function getShareByToken($token);
190
+
191
+    /**
192
+     * Verify the password of a public share
193
+     *
194
+     * @param IShare $share
195
+     * @param ?string $password
196
+     * @return bool
197
+     * @since 9.0.0
198
+     */
199
+    public function checkPassword(IShare $share, $password);
200
+
201
+    /**
202
+     * The user with UID is deleted.
203
+     * All share providers have to cleanup the shares with this user as well
204
+     * as shares owned by this user.
205
+     * Shares only initiated by this user are fine.
206
+     *
207
+     * @param string $uid
208
+     * @since 9.1.0
209
+     */
210
+    public function userDeleted($uid);
211
+
212
+    /**
213
+     * The group with $gid is deleted
214
+     * We need to clear up all shares to this group
215
+     *
216
+     * @param string $gid
217
+     * @since 9.1.0
218
+     */
219
+    public function groupDeleted($gid);
220
+
221
+    /**
222
+     * The user $uid is deleted from the group $gid
223
+     * All user specific group shares have to be removed
224
+     *
225
+     * @param string $uid
226
+     * @param string $gid
227
+     * @since 9.1.0
228
+     */
229
+    public function userDeletedFromGroup($uid, $gid);
230
+
231
+    /**
232
+     * Get access list to a path. This means
233
+     * all the users that can access a given path.
234
+     *
235
+     * Consider:
236
+     * -root
237
+     * |-folder1 (23)
238
+     *  |-folder2 (32)
239
+     *   |-fileA (42)
240
+     *
241
+     * fileA is shared with user1 and user1@server1 and email1@maildomain1
242
+     * folder2 is shared with group2 (user4 is a member of group2)
243
+     * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
244
+     *                        and email2@maildomain2
245
+     *
246
+     * Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
247
+     * [
248
+     *  users  => [
249
+     *      'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
250
+     *      'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
251
+     *      'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
252
+     *  ],
253
+     *  remote => [
254
+     *      'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
255
+     *      'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
256
+     *  ],
257
+     *  public => bool
258
+     *  mail => [
259
+     *      'email1@maildomain1' => ['node_id' => 42, 'token' => 'aBcDeFg'],
260
+     *      'email2@maildomain2' => ['node_id' => 23, 'token' => 'hIjKlMn'],
261
+     *  ]
262
+     *
263
+     * The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
264
+     * [
265
+     *  users  => ['user1', 'user2', 'user4'],
266
+     *  remote => bool,
267
+     *  public => bool
268
+     *  mail => ['email1@maildomain1', 'email2@maildomain2']
269
+     * ]
270
+     *
271
+     * This is required for encryption/activity
272
+     *
273
+     * @param \OCP\Files\Node $path
274
+     * @param bool $recursive Should we check all parent folders as well
275
+     * @param bool $currentAccess Should the user have currently access to the file
276
+     * @return array
277
+     * @since 12
278
+     */
279
+    public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false);
280
+
281
+    /**
282
+     * Instantiates a new share object. This is to be passed to
283
+     * createShare.
284
+     *
285
+     * @return IShare
286
+     * @since 9.0.0
287
+     */
288
+    public function newShare();
289
+
290
+    /**
291
+     * Is the share API enabled
292
+     *
293
+     * @return bool
294
+     * @since 9.0.0
295
+     */
296
+    public function shareApiEnabled();
297
+
298
+    /**
299
+     * Is public link sharing enabled
300
+     *
301
+     * @param ?IUser $user User to check against group exclusions, defaults to current session user
302
+     * @return bool
303
+     * @since 9.0.0
304
+     * @since 33.0.0 Added optional $user parameter
305
+     */
306
+    public function shareApiAllowLinks(?IUser $user = null);
307
+
308
+    /**
309
+     * Is password on public link required
310
+     *
311
+     * @param bool $checkGroupMembership Check group membership exclusion
312
+     * @return bool
313
+     * @since 9.0.0
314
+     * @since 24.0.0 Added optional $checkGroupMembership parameter
315
+     */
316
+    public function shareApiLinkEnforcePassword(bool $checkGroupMembership = true);
317
+
318
+    /**
319
+     * Is default expire date enabled
320
+     *
321
+     * @return bool
322
+     * @since 9.0.0
323
+     */
324
+    public function shareApiLinkDefaultExpireDate();
325
+
326
+    /**
327
+     * Is default expire date enforced
328
+     *`
329
+     * @return bool
330
+     * @since 9.0.0
331
+     */
332
+    public function shareApiLinkDefaultExpireDateEnforced();
333
+
334
+    /**
335
+     * Number of default expire days
336
+     *
337
+     * @return int
338
+     * @since 9.0.0
339
+     */
340
+    public function shareApiLinkDefaultExpireDays();
341
+
342
+    /**
343
+     * Is default internal expire date enabled
344
+     *
345
+     * @return bool
346
+     * @since 22.0.0
347
+     */
348
+    public function shareApiInternalDefaultExpireDate(): bool;
349
+
350
+    /**
351
+     * Is default remote expire date enabled
352
+     *
353
+     * @return bool
354
+     * @since 22.0.0
355
+     */
356
+    public function shareApiRemoteDefaultExpireDate(): bool;
357
+
358
+    /**
359
+     * Is default expire date enforced
360
+     *
361
+     * @return bool
362
+     * @since 22.0.0
363
+     */
364
+    public function shareApiInternalDefaultExpireDateEnforced(): bool;
365
+
366
+    /**
367
+     * Is default expire date enforced for remote shares
368
+     *
369
+     * @return bool
370
+     * @since 22.0.0
371
+     */
372
+    public function shareApiRemoteDefaultExpireDateEnforced(): bool;
373
+
374
+    /**
375
+     * Number of default expire days
376
+     *
377
+     * @return int
378
+     * @since 22.0.0
379
+     */
380
+    public function shareApiInternalDefaultExpireDays(): int;
381
+
382
+    /**
383
+     * Number of default expire days for remote shares
384
+     *
385
+     * @return int
386
+     * @since 22.0.0
387
+     */
388
+    public function shareApiRemoteDefaultExpireDays(): int;
389
+
390
+    /**
391
+     * Allow public upload on link shares
392
+     *
393
+     * @return bool
394
+     * @since 9.0.0
395
+     */
396
+    public function shareApiLinkAllowPublicUpload();
397
+
398
+    /**
399
+     * check if user can only share with group members
400
+     * @return bool
401
+     * @since 9.0.0
402
+     */
403
+    public function shareWithGroupMembersOnly();
404
+
405
+    /**
406
+     * If shareWithGroupMembersOnly is enabled, return an optional
407
+     * list of groups that must be excluded from the principle of
408
+     * belonging to the same group.
409
+     * @return array
410
+     * @since 27.0.0
411
+     */
412
+    public function shareWithGroupMembersOnlyExcludeGroupsList();
413
+
414
+    /**
415
+     * Check if users can share with groups
416
+     * @return bool
417
+     * @since 9.0.1
418
+     */
419
+    public function allowGroupSharing();
420
+
421
+    /**
422
+     * Check if user enumeration is allowed
423
+     *
424
+     * @return bool
425
+     * @since 19.0.0
426
+     */
427
+    public function allowEnumeration(): bool;
428
+
429
+    /**
430
+     * Check if user enumeration is limited to the users groups
431
+     *
432
+     * @return bool
433
+     * @since 19.0.0
434
+     */
435
+    public function limitEnumerationToGroups(): bool;
436
+
437
+    /**
438
+     * Check if user enumeration is limited to the phonebook matches
439
+     *
440
+     * @return bool
441
+     * @since 21.0.1
442
+     */
443
+    public function limitEnumerationToPhone(): bool;
444
+
445
+    /**
446
+     * Check if user enumeration is allowed to return also on full match
447
+     * and ignore limitations to phonebook or groups.
448
+     *
449
+     * @return bool
450
+     * @since 21.0.1
451
+     */
452
+    public function allowEnumerationFullMatch(): bool;
453
+
454
+    /**
455
+     * When `allowEnumerationFullMatch` is enabled and `matchEmail` is set,
456
+     * then also return results for full email matches.
457
+     *
458
+     * @return bool
459
+     * @since 25.0.0
460
+     */
461
+    public function matchEmail(): bool;
462
+
463
+    /**
464
+     * When `allowEnumerationFullMatch` is enabled and `matchUserId` is set,
465
+     * then also return results for full user id matches.
466
+     *
467
+     * @return bool
468
+     * @since 33.0.0
469
+     */
470
+    public function matchUserId(): bool;
471
+
472
+    /**
473
+     * When `allowEnumerationFullMatch` is enabled and `ignoreSecondDisplayName` is set,
474
+     * then the search should ignore matches on the second displayname and only use the first.
475
+     *
476
+     * @return bool
477
+     * @since 25.0.0
478
+     */
479
+    public function ignoreSecondDisplayName(): bool;
480
+
481
+
482
+    /**
483
+     * Check if custom tokens are allowed
484
+     *
485
+     * @since 31.0.0
486
+     */
487
+    public function allowCustomTokens(): bool;
488
+
489
+    /**
490
+     * Check if the current user can view the share
491
+     * even if the download is disabled.
492
+     *
493
+     * @since 32.0.0
494
+     */
495
+    public function allowViewWithoutDownload(): bool;
496
+
497
+    /**
498
+     * Check if the current user can enumerate the target user
499
+     *
500
+     * @param IUser|null $currentUser
501
+     * @param IUser $targetUser
502
+     * @return bool
503
+     * @since 23.0.0
504
+     */
505
+    public function currentUserCanEnumerateTargetUser(?IUser $currentUser, IUser $targetUser): bool;
506
+
507
+    /**
508
+     * Check if sharing is disabled for the given user
509
+     *
510
+     * @since 9.0.0
511
+     */
512
+    public function sharingDisabledForUser(?string $userId): bool;
513
+
514
+    /**
515
+     * Check if outgoing server2server shares are allowed
516
+     * @return bool
517
+     * @since 9.0.0
518
+     */
519
+    public function outgoingServer2ServerSharesAllowed();
520
+
521
+    /**
522
+     * Check if outgoing server2server shares are allowed
523
+     * @return bool
524
+     * @since 14.0.0
525
+     */
526
+    public function outgoingServer2ServerGroupSharesAllowed();
527
+
528
+
529
+    /**
530
+     * Check if a given share provider exists
531
+     * @param int $shareType
532
+     * @return bool
533
+     * @since 11.0.0
534
+     */
535
+    public function shareProviderExists($shareType);
536
+
537
+    /**
538
+     * @param string $shareProviderClass
539
+     * @since 21.0.0
540
+     */
541
+    public function registerShareProvider(string $shareProviderClass): void;
542
+
543
+    /**
544
+     * @Internal
545
+     *
546
+     * Get all the shares as iterable to reduce memory overhead
547
+     * Note, since this opens up database cursors the iterable should
548
+     * be fully itterated.
549
+     *
550
+     * @return iterable
551
+     * @since 18.0.0
552
+     */
553
+    public function getAllShares(): iterable;
554
+
555
+    /**
556
+     * Generate a unique share token
557
+     *
558
+     * @throws ShareTokenException Failed to generate a unique token
559
+     * @since 31.0.0
560
+     */
561
+    public function generateToken(): string;
562 562
 }
Please login to merge, or discard this patch.